util_hbm: Initial version

This IP serves as storage interfacing element for external memories like
HBM or DDR4 which have AXI3 or AXI4 data interfaces.

The core leverages the axi_dmac as building blocks by merging an array of
simplex DMA channels into duplex AXI channels. The core will split the
incoming data from the source AXIS interface to multiple AXI channels,
and in the read phase will merge the multiple AXI channels into a single
AXIS destination interface.
The number of duplex channels is set by syntheses parameter and must be
set with the ratio of AXIS and AXI3/4 interface.

Underflow or Overflow conditions are reported back to the data offload
through the control/status interface.

In case multiple AXI channels are used the source and destination AXIS
interfaces widths must match.
main
Laszlo Nagy 2022-02-14 15:13:14 +00:00 committed by Laszlo Nagy
parent a0098acc5a
commit 3bf7b6c80f
7 changed files with 1464 additions and 0 deletions

21
library/util_hbm/Makefile Normal file
View File

@ -0,0 +1,21 @@
####################################################################################
## Copyright (c) 2018 - 2021 Analog Devices, Inc.
### SPDX short identifier: BSD-1-Clause
## Auto-generated, do not modify!
####################################################################################
LIBRARY_NAME := util_hbm
GENERIC_DEPS += util_hbm.v
XILINX_DEPS += bd/bd.tcl
XILINX_DEPS += util_hbm_constr.xdc
XILINX_DEPS += util_hbm_ip.tcl
XILINX_DEPS += util_hbm_ooc.ttcl
XILINX_DEPS += ../interfaces/if_do_ctrl.xml
XILINX_DEPS += ../interfaces/if_do_ctrl_rtl.xml
XILINX_INTERFACE_DEPS += interfaces
include ../scripts/library.mk

View File

@ -0,0 +1,86 @@
proc init {cellpath otherInfo} {
set ip [get_bd_cells $cellpath]
bd::mark_propagate_only $ip \
"AXI_ADDR_WIDTH"
}
# Executed when you close the config window
proc post_config_ip {cellpath otherinfo} {
set ip [get_bd_cells $cellpath]
# Update AXI interface properties according to configuration
set axi_protocol [get_property "CONFIG.AXI_PROTOCOL" $ip]
set data_width [get_property "CONFIG.AXI_DATA_WIDTH" $ip]
set src_fifo_size [get_property "CONFIG.SRC_FIFO_SIZE" $ip]
set dst_fifo_size [get_property "CONFIG.DST_FIFO_SIZE" $ip]
if {$axi_protocol == 0} {
set axi_protocol_str "AXI4"
set max_beats_per_burst 256
} else {
set axi_protocol_str "AXI3"
set max_beats_per_burst 16
}
set num_m [get_property "CONFIG.NUM_M" $ip]
for {set idx 0} {$idx < $num_m} {incr idx} {
set intf [get_bd_intf_pins [format "%s/MAXI_%d" $cellpath $idx]]
set_property CONFIG.PROTOCOL $axi_protocol_str $intf
set_property CONFIG.MAX_BURST_LENGTH $max_beats_per_burst $intf
set_property CONFIG.NUM_WRITE_OUTSTANDING $src_fifo_size $intf
set_property CONFIG.NUM_READ_OUTSTANDING $dst_fifo_size $intf
}
# For multi master configurations (e.g.HBM) the AXIS data widths must match
if { $num_m > 1} {
set src_width [get_property "CONFIG.SRC_DATA_WIDTH" $ip]
set dst_width [get_property "CONFIG.DST_DATA_WIDTH" $ip]
if {$src_width != $dst_width} {
bd::send_msg -of $cellpath -type ERROR -msg_id 1 -text ": For multi AXI master configuration the Source AXIS interface width ($src_width) must match the Destination AXIS interface width ($dst_width) ."
} else {
# AXIS Data widths divided by number of masters must be >= 8 and power of 2
set bits_per_master [expr $src_width/$num_m]
if {$bits_per_master < 8} {
bd::send_msg -of $cellpath -type ERROR -msg_id 2 -text ": Number of AXI masters ($num_m) too high. AXIS data widths divided by number of masters ($src_width / $num_m = $bits_per_master) must be >= 8 ."
}
}
}
}
proc log2 {x} {
return [tcl::mathfunc::int [tcl::mathfunc::ceil [expr [tcl::mathfunc::log $x] / [tcl::mathfunc::log 2]]]]
}
# Executed when the block design is validated
proc propagate {cellpath otherinfo} {
set ip [get_bd_cells $cellpath]
}
proc post_propagate {cellpath otherinfo} {
set ip [get_bd_cells $cellpath]
#Check address space
set length_width [get_property "CONFIG.LENGTH_WIDTH" $ip]
set axi_addr_width [get_property "CONFIG.AXI_ADDR_WIDTH" $ip]
set ddr_base_adddress [get_property "CONFIG.DDR_BASE_ADDDRESS" $ip]
set hbm_segment_index [get_property "CONFIG.HBM_SEGMENT_INDEX" $ip]
set mem_type [get_property "CONFIG.MEM_TYPE" $ip]
if {$mem_type == 1} {
set addr_width [log2 [expr $ddr_base_adddress + 2 ** $length_width - 1]]
} else {
# assumption: 1 segmetn is 256MB
set addr_width [log2 [expr $hbm_segment_index * 256 * 1024 * 1024 + 2 ** $length_width - 1]]
}
set_property "CONFIG.AXI_ADDR_WIDTH" $addr_width $ip
bd::send_msg -of $cellpath -type INFO -msg_id 2 -text ": AXI Address Width set to $addr_width"
}

View File

@ -0,0 +1,117 @@
# density 4GB,8GB
proc ad_create_hbm {ip_name {density "4GB"}} {
global hbm_sim;
if { [info exists hbm_sim] == 0} {
set hbm_sim 0
}
if {$hbm_sim == 0} {
ad_ip_instance hbm $ip_name
ad_ip_parameter $ip_name CONFIG.USER_HBM_DENSITY $density
ad_ip_parameter $ip_name CONFIG.USER_APB_EN {false}
set i_hbm_ip [get_bd_cells $ip_name]
set num_stacks [get_property CONFIG.USER_HBM_STACK $i_hbm_ip]
# 16 pseudo channels / sections / segments per stack
set num_segments [expr $num_stacks*16]
for {set i 1} {$i < $num_segments} {incr i} {
set i_formatted [format "%02d" $i]
ad_ip_parameter $ip_name CONFIG.USER_SAXI_${i_formatted} {false}
}
} else {
# Create data storage HMB controller model (AXI slave)
ad_ip_instance axi_vip $ip_name [list \
INTERFACE_MODE {SLAVE} \
]
adi_sim_add_define "HBM_AXI=$ip_name"
}
}
proc ad_create_util_hbm {name rx_tx_n src_width dst_width mem_size {axi_data_width 256} {mem_type 2}} {
if {$mem_type == 2} {
# HBM
# split converter side bus into multiple AXI masters
set number_of_masters [expr int(ceil((${rx_tx_n} == 1 ? ${dst_width}.0 : ${src_width}.0) / ${axi_data_width}.0))]
} else {
# DDR we have always one master
set number_of_masters 1
}
ad_ip_instance util_hbm $name [list \
LENGTH_WIDTH [log2 $mem_size] \
SRC_DATA_WIDTH $src_width \
DST_DATA_WIDTH $dst_width \
AXI_DATA_WIDTH $axi_data_width \
TX_RX_N $rx_tx_n \
NUM_M $number_of_masters \
MEM_TYPE $mem_type \
]
}
proc ad_connect_hbm {i_hbm i_util_hbm axi_clk axi_aresetn {first_slave_index 0}} {
global hbm_sim;
if { [info exists hbm_sim] == 0} {
set hbm_sim 0
}
set i_util_hbm_ip [get_bd_cells $i_util_hbm]
set segments_per_master [get_property CONFIG.HBM_SEGMENTS_PER_MASTER $i_util_hbm_ip]
set num_m [get_property CONFIG.NUM_M $i_util_hbm_ip]
if {$hbm_sim == 0} {
set i_hbm_ip [get_bd_cells $i_hbm]
set num_stacks [get_property CONFIG.USER_HBM_STACK $i_hbm_ip]
# 16 pseudo channels / sections / segments per stack
set num_segments [expr $num_stacks*16]
set totat_used_segments [expr $num_m * $segments_per_master]
for {set i 0} {$i < $totat_used_segments} {incr i} {
set idx_hbm_slv [format "%02d" [expr $i+$first_slave_index]]
if {$i % $segments_per_master == 0} {
ad_ip_parameter $i_hbm CONFIG.USER_SAXI_${idx_hbm_slv} {true}
ad_connect $i_util_hbm/MAXI_[expr $i/$segments_per_master] $i_hbm/SAXI_${idx_hbm_slv}
ad_connect $i_hbm/AXI_${idx_hbm_slv}_ACLK $axi_clk
ad_connect $i_hbm/AXI_${idx_hbm_slv}_ARESET_N $axi_aresetn
}
}
ad_ip_parameter $i_util_hbm CONFIG.HBM_SEGMENT_INDEX $first_slave_index
} else {
# Create smart connect
ad_ip_instance smartconnect axi_hbm_interconnect [list \
NUM_MI 1 \
NUM_SI $num_m \
]
# connect it to hbm vip
ad_connect axi_hbm_interconnect/M00_AXI $i_hbm/S_AXI
# connect smart connect to util_hbm
for {set i 0} {$i < $num_m} {incr i} {
set i_formatted [format "%02d" $i]
ad_connect $i_util_hbm/MAXI_$i axi_hbm_interconnect/S${i_formatted}_AXI
}
ad_connect axi_hbm_interconnect/aclk $axi_clk
ad_connect axi_hbm_interconnect/aresetn $axi_aresetn
ad_connect $i_hbm/aclk $axi_clk
ad_connect $i_hbm/aresetn $axi_aresetn
}
ad_connect $axi_clk $i_util_hbm/m_axi_aclk
ad_connect $axi_aresetn $i_util_hbm/m_axi_aresetn
for {set i 0} {$i < $num_m} {incr i} {
assign_bd_address -target_address_space $i_util_hbm/MAXI_${i}
}
}
proc log2 {x} {
return [tcl::mathfunc::int [tcl::mathfunc::ceil [expr [tcl::mathfunc::log $x] / [tcl::mathfunc::log 2]]]]
}

574
library/util_hbm/util_hbm.v Normal file
View File

@ -0,0 +1,574 @@
// ***************************************************************************
// ***************************************************************************
// Copyright 2014 - 2022 (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.
//
// ***************************************************************************
// ***************************************************************************
// This IP serves as storage interfacing element for external memories like
// HBM or DDR4 which have AXI3 or AXI4 data interfaces.
//
// The core leverages the axi_dmac as building blocks by merging an array of
// simplex DMA channels into duplex AXI channels. The core will split the
// incoming data from the source AXIS interface to multiple AXI channels,
// and in the read phase will merge the multiple AXI channels into a single
// AXIS destination interface.
// The number of duplex channels is set by syntheses parameter and must be
// set with the ratio of AXIS and AXI3/4 interface.
//
// Underflow or Overflow conditions are reported back to the data offload
// through the control/status interface.
//
// Constraints:
// min(SRC_DATA_WIDTH,DST_DATA_WIDTH) / NUM_M >= 8
// In case multiple AXI channels are used the source and destination AXIS
// interfaces widths must match.
`timescale 1ns/100ps
module util_hbm #(
parameter TX_RX_N = 1,
parameter SRC_DATA_WIDTH = 512,
parameter DST_DATA_WIDTH = 512,
parameter LENGTH_WIDTH = 32,
// Memory interface parameters
parameter AXI_PROTOCOL = 0, // 0 - AXI4 ; 1 - AXI3
parameter AXI_DATA_WIDTH = 256,
parameter AXI_ADDR_WIDTH = 32,
parameter MEM_TYPE = 2, // 1 - DDR ; 2 - HBM
// This will size the storage per master where each segment is 256MB
parameter HBM_SEGMENTS_PER_MASTER = 4,
parameter HBM_SEGMENT_INDEX = 0,
// DDR parameters
parameter DDR_BASE_ADDDRESS = 0,
// Number of AXI masters
parameter NUM_M = 2,
// Data mover parameters
parameter SRC_FIFO_SIZE = 8, // In AXI bursts
parameter DST_FIFO_SIZE = 8
) (
input wr_request_enable,
input wr_request_valid,
output wr_request_ready,
input [LENGTH_WIDTH-1:0] wr_request_length,
output [LENGTH_WIDTH-1:0] wr_response_measured_length,
output reg wr_response_eot = 1'b0,
output wr_overflow,
input rd_request_enable,
input rd_request_valid,
output rd_request_ready,
input [LENGTH_WIDTH-1:0] rd_request_length,
output reg rd_response_eot = 1'b0,
output rd_underflow,
// Slave streaming AXI interface
input s_axis_aclk,
input s_axis_aresetn,
output s_axis_ready,
input s_axis_valid,
input [SRC_DATA_WIDTH-1:0] s_axis_data,
input [SRC_DATA_WIDTH/8-1:0] s_axis_strb,
input [SRC_DATA_WIDTH/8-1:0] s_axis_keep,
input [0:0] s_axis_user,
input s_axis_last,
// Master streaming AXI interface
input m_axis_aclk,
input m_axis_aresetn,
input m_axis_ready,
output m_axis_valid,
output [DST_DATA_WIDTH-1:0] m_axis_data,
output [DST_DATA_WIDTH/8-1:0] m_axis_strb,
output [DST_DATA_WIDTH/8-1:0] m_axis_keep,
output [0:0] m_axis_user,
output m_axis_last,
// Master AXI3 interface
input m_axi_aclk,
input m_axi_aresetn,
// Write address
output [NUM_M*AXI_ADDR_WIDTH-1:0] m_axi_awaddr,
output [NUM_M*(8-(4*AXI_PROTOCOL))-1:0] m_axi_awlen,
output [NUM_M*3-1:0] m_axi_awsize,
output [NUM_M*2-1:0] m_axi_awburst,
output [NUM_M-1:0] m_axi_awvalid,
input [NUM_M-1:0] m_axi_awready,
// Write data
output [NUM_M*AXI_DATA_WIDTH-1:0] m_axi_wdata,
output [NUM_M*(AXI_DATA_WIDTH/8)-1:0] m_axi_wstrb,
input [NUM_M-1:0] m_axi_wready,
output [NUM_M-1:0] m_axi_wvalid,
output [NUM_M-1:0] m_axi_wlast,
// Write response
input [NUM_M-1:0] m_axi_bvalid,
input [NUM_M*2-1:0] m_axi_bresp,
output [NUM_M-1:0] m_axi_bready,
// Read address
input [NUM_M-1:0] m_axi_arready,
output [NUM_M-1:0] m_axi_arvalid,
output [NUM_M*AXI_ADDR_WIDTH-1:0] m_axi_araddr,
output [NUM_M*(8-(4*AXI_PROTOCOL))-1:0] m_axi_arlen,
output [NUM_M*3-1:0] m_axi_arsize,
output [NUM_M*2-1:0] m_axi_arburst,
// Read data and response
input [NUM_M*AXI_DATA_WIDTH-1:0] m_axi_rdata,
output [NUM_M-1:0] m_axi_rready,
input [NUM_M-1:0] m_axi_rvalid,
input [NUM_M*2-1:0] m_axi_rresp,
input [NUM_M-1:0] m_axi_rlast
);
localparam DMA_TYPE_AXI_MM = 0;
localparam DMA_TYPE_AXI_STREAM = 1;
localparam DMA_TYPE_FIFO = 2;
localparam SRC_DATA_WIDTH_PER_M = SRC_DATA_WIDTH / NUM_M;
localparam DST_DATA_WIDTH_PER_M = DST_DATA_WIDTH / NUM_M;
localparam AXI_BYTES_PER_BEAT_WIDTH = $clog2(AXI_DATA_WIDTH/8);
localparam SRC_BYTES_PER_BEAT_WIDTH = $clog2(SRC_DATA_WIDTH_PER_M/8);
localparam DST_BYTES_PER_BEAT_WIDTH = $clog2(DST_DATA_WIDTH_PER_M/8);
// Size bursts to the max possible size
// AXI 3 1 burst is 16 beats
// AXI 4 1 burst is 256 beats
// Limit one burst to 4096 bytes
localparam MAX_BYTES_PER_BURST = (AXI_PROTOCOL ? 16 : 256) * AXI_DATA_WIDTH/8;
localparam MAX_BYTES_PER_BURST_LMT = MAX_BYTES_PER_BURST >= 4096 ? 4096 :
MAX_BYTES_PER_BURST;
localparam BYTES_PER_BURST_WIDTH = $clog2(MAX_BYTES_PER_BURST_LMT);
localparam AXI_ALEN = (8-(4*AXI_PROTOCOL));
localparam NUM_M_LOG2 = $clog2(NUM_M);
genvar i;
wire [NUM_M-1:0] wr_request_ready_loc;
wire [NUM_M-1:0] rd_request_ready_loc;
wire [NUM_M-1:0] wr_request_eot_loc;
wire [NUM_M-1:0] rd_request_eot_loc;
wire [NUM_M-1:0] rd_response_valid_loc;
wire [NUM_M-1:0] wr_response_valid_loc;
wire wr_eot_pending_all;
wire rd_eot_pending_all;
assign wr_request_ready = &wr_request_ready_loc;
assign rd_request_ready = &rd_request_ready_loc;
// Aggregate end of transfer from all masters
reg [NUM_M-1:0] wr_eot_pending = {NUM_M{1'b0}};
reg [NUM_M-1:0] rd_eot_pending = {NUM_M{1'b0}};
assign wr_eot_pending_all = &wr_eot_pending;
assign rd_eot_pending_all = &rd_eot_pending;
wire [NUM_M-1:0] s_axis_ready_loc;
assign s_axis_ready = &s_axis_ready_loc;
wire [NUM_M-1:0] m_axis_last_loc;
assign m_axis_last = &m_axis_last_loc;
wire [NUM_M-1:0] m_axis_valid_loc;
assign m_axis_valid = &m_axis_valid_loc;
wire [NUM_M-1:0] wr_response_ready_loc;
wire [NUM_M-1:0] rd_response_ready_loc;
wire [NUM_M-1:0] wr_overflow_loc;
wire [NUM_M-1:0] rd_underflow_loc;
// Measure stored data in case transfer is shorter than programmed,
// do the measurement only with the first master, all others should be
// similar.
localparam LW_PER_M = LENGTH_WIDTH-NUM_M_LOG2;
wire [NUM_M*BYTES_PER_BURST_WIDTH-1:0] wr_measured_burst_length;
reg [LW_PER_M-1:0] wr_response_measured_length_per_m = 'h0;
always @(posedge s_axis_aclk) begin
if (wr_request_enable == 1'b0) begin
wr_response_measured_length_per_m <= {LW_PER_M{1'h0}};
end else if (wr_response_valid_loc[0] == 1'b1 && wr_response_ready_loc[0] == 1'b1) begin
wr_response_measured_length_per_m <= wr_response_measured_length_per_m +
{{LW_PER_M-BYTES_PER_BURST_WIDTH{1'b0}},wr_measured_burst_length[BYTES_PER_BURST_WIDTH-1:0]} +
{{LW_PER_M-1{1'b0}},~wr_request_eot_loc[0]};
end else if (wr_response_eot == 1'b1) begin
wr_response_measured_length_per_m <= {LW_PER_M{1'h0}};
end
end
assign wr_response_measured_length = {wr_response_measured_length_per_m,{NUM_M_LOG2{1'b1}}};
always @(posedge s_axis_aclk) begin
wr_response_eot <= wr_eot_pending_all;
end
always @(posedge m_axis_aclk) begin
rd_response_eot <= rd_eot_pending_all;
end
generate
for (i = 0; i < NUM_M; i=i+1) begin
wire [11:0] rd_dbg_status;
wire rd_needs_reset;
wire s_axis_xfer_req;
wire m_axis_xfer_req;
reg rd_needs_reset_d = 1'b0;
// 2Gb (256MB) per segment
localparam ADDR_OFFSET = (MEM_TYPE == 1) ? DDR_BASE_ADDDRESS :
(HBM_SEGMENT_INDEX+i) * HBM_SEGMENTS_PER_MASTER * 256 * 1024 * 1024 ;
always @(posedge s_axis_aclk) begin
if (wr_eot_pending_all) begin
wr_eot_pending[i] <= 1'b0;
end else if (wr_request_eot_loc[i] & wr_response_valid_loc[i]) begin
wr_eot_pending[i] <= 1'b1;
end
end
// For last burst wait until all masters are done
assign wr_response_ready_loc[i] = wr_request_eot_loc[i] ? wr_eot_pending_all : wr_response_valid_loc[i];
// Overflow whenever s_axis_ready deasserts during capture (RX_PATH)
assign wr_overflow_loc[i] = TX_RX_N[0] ? 1'b0 : s_axis_xfer_req & ~s_axis_ready_loc[i];
// AXIS to AXI3
axi_dmac_transfer #(
.DMA_DATA_WIDTH_SRC(SRC_DATA_WIDTH_PER_M),
.DMA_DATA_WIDTH_DEST(AXI_DATA_WIDTH),
.DMA_LENGTH_WIDTH(LENGTH_WIDTH),
.DMA_LENGTH_ALIGN(SRC_BYTES_PER_BEAT_WIDTH),
.BYTES_PER_BEAT_WIDTH_DEST(AXI_BYTES_PER_BEAT_WIDTH),
.BYTES_PER_BEAT_WIDTH_SRC(SRC_BYTES_PER_BEAT_WIDTH),
.BYTES_PER_BURST_WIDTH(BYTES_PER_BURST_WIDTH),
.DMA_TYPE_DEST(DMA_TYPE_AXI_MM),
.DMA_TYPE_SRC(DMA_TYPE_AXI_STREAM),
.DMA_AXI_ADDR_WIDTH(AXI_ADDR_WIDTH),
.DMA_2D_TRANSFER(1'b0),
.ASYNC_CLK_REQ_SRC(0),
.ASYNC_CLK_SRC_DEST(1),
.ASYNC_CLK_DEST_REQ(1),
.AXI_SLICE_DEST(1),
.AXI_SLICE_SRC(1),
.MAX_BYTES_PER_BURST(MAX_BYTES_PER_BURST_LMT),
.FIFO_SIZE(SRC_FIFO_SIZE),
.ID_WIDTH($clog2(SRC_FIFO_SIZE)),
.AXI_LENGTH_WIDTH_SRC(8-(4*AXI_PROTOCOL)),
.AXI_LENGTH_WIDTH_DEST(8-(4*AXI_PROTOCOL)),
.ENABLE_DIAGNOSTICS_IF(0),
.ALLOW_ASYM_MEM(1)
) i_wr_transfer (
.ctrl_clk(s_axis_aclk),
.ctrl_resetn(s_axis_aresetn),
// Control interface
.ctrl_enable(wr_request_enable),
.ctrl_pause(1'b0),
.req_valid(wr_request_valid),
.req_ready(wr_request_ready_loc[i]),
.req_dest_address(ADDR_OFFSET[AXI_ADDR_WIDTH-1:AXI_BYTES_PER_BEAT_WIDTH]),
.req_src_address('h0),
.req_x_length(wr_request_length >> NUM_M_LOG2),
.req_y_length(0),
.req_dest_stride(0),
.req_src_stride(0),
.req_sync_transfer_start(1'b0),
.req_last(1'b1),
.req_eot(wr_request_eot_loc[i]),
.req_measured_burst_length(wr_measured_burst_length[BYTES_PER_BURST_WIDTH*i+:BYTES_PER_BURST_WIDTH]),
.req_response_partial(),
.req_response_valid(wr_response_valid_loc[i]),
.req_response_ready(wr_response_ready_loc[i]),
.m_dest_axi_aclk(m_axi_aclk),
.m_dest_axi_aresetn(m_axi_aresetn),
.m_src_axi_aclk(1'b0),
.m_src_axi_aresetn(1'b0),
.m_axi_awaddr(m_axi_awaddr[AXI_ADDR_WIDTH*i+:AXI_ADDR_WIDTH]),
.m_axi_awlen(m_axi_awlen[AXI_ALEN*i+:AXI_ALEN]),
.m_axi_awsize(m_axi_awsize[3*i+:3]),
.m_axi_awburst(m_axi_awburst[2*i+:2]),
.m_axi_awprot(),
.m_axi_awcache(),
.m_axi_awvalid(m_axi_awvalid[i]),
.m_axi_awready(m_axi_awready[i]),
.m_axi_wdata(m_axi_wdata[AXI_DATA_WIDTH*i+:AXI_DATA_WIDTH]),
.m_axi_wstrb(m_axi_wstrb[(AXI_DATA_WIDTH/8)*i+:(AXI_DATA_WIDTH/8)]),
.m_axi_wready(m_axi_wready[i]),
.m_axi_wvalid(m_axi_wvalid[i]),
.m_axi_wlast(m_axi_wlast[i]),
.m_axi_bvalid(m_axi_bvalid[i]),
.m_axi_bresp(m_axi_bresp[2*i+:2]),
.m_axi_bready(m_axi_bready[i]),
.m_axi_arready(),
.m_axi_arvalid(),
.m_axi_araddr(),
.m_axi_arlen(),
.m_axi_arsize(),
.m_axi_arburst(),
.m_axi_arprot(),
.m_axi_arcache(),
.m_axi_rdata(),
.m_axi_rready(),
.m_axi_rvalid(),
.m_axi_rlast(),
.m_axi_rresp(),
.s_axis_aclk(s_axis_aclk),
.s_axis_ready(s_axis_ready_loc[i]),
.s_axis_valid(s_axis_valid),
.s_axis_data(s_axis_data[SRC_DATA_WIDTH_PER_M*i+:SRC_DATA_WIDTH_PER_M]),
.s_axis_user(s_axis_user),
.s_axis_last(s_axis_last),
.s_axis_xfer_req(s_axis_xfer_req),
.m_axis_aclk(1'b0),
.m_axis_ready(1'b1),
.m_axis_valid(),
.m_axis_data(),
.m_axis_last(),
.m_axis_xfer_req(),
.fifo_wr_clk(1'b0),
.fifo_wr_en(1'b0),
.fifo_wr_din('b0),
.fifo_wr_overflow(),
.fifo_wr_sync(),
.fifo_wr_xfer_req(),
.fifo_rd_clk(1'b0),
.fifo_rd_en(1'b0),
.fifo_rd_valid(),
.fifo_rd_dout(),
.fifo_rd_underflow(),
.fifo_rd_xfer_req(),
// DBG
.dbg_dest_request_id(),
.dbg_dest_address_id(),
.dbg_dest_data_id(),
.dbg_dest_response_id(),
.dbg_src_request_id(),
.dbg_src_address_id(),
.dbg_src_data_id(),
.dbg_src_response_id(),
.dbg_status(),
.dest_diag_level_bursts()
);
always @(posedge m_axis_aclk) begin
rd_needs_reset_d <= rd_needs_reset;
end
// Generate an end of transfer at the end of flush marked by rd_needs_reset
always @(posedge m_axis_aclk) begin
if (rd_eot_pending_all) begin
rd_eot_pending[i] <= 1'b0;
end else if ((rd_request_eot_loc[i] & rd_response_valid_loc[i]) ||
(~rd_needs_reset & rd_needs_reset_d)) begin
rd_eot_pending[i] <= 1'b1;
end
end
assign rd_response_ready_loc[i] = rd_request_eot_loc[i] ? rd_eot_pending_all : rd_response_valid_loc[i];
// Underflow whenever m_axis_valid deasserts during play (TX_PATH)
assign rd_underflow_loc[i] = ~TX_RX_N[0] ? 1'b0 : m_axis_xfer_req & m_axis_ready & ~m_axis_valid_loc[i];
// AXI3 to MAXIS
axi_dmac_transfer #(
.DMA_DATA_WIDTH_SRC(AXI_DATA_WIDTH),
.DMA_DATA_WIDTH_DEST(DST_DATA_WIDTH_PER_M),
.DMA_LENGTH_WIDTH(LENGTH_WIDTH),
.DMA_LENGTH_ALIGN(DST_BYTES_PER_BEAT_WIDTH),
.BYTES_PER_BEAT_WIDTH_DEST(DST_BYTES_PER_BEAT_WIDTH),
.BYTES_PER_BEAT_WIDTH_SRC(AXI_BYTES_PER_BEAT_WIDTH),
.BYTES_PER_BURST_WIDTH(BYTES_PER_BURST_WIDTH),
.DMA_TYPE_DEST(DMA_TYPE_AXI_STREAM),
.DMA_TYPE_SRC(DMA_TYPE_AXI_MM),
.DMA_AXI_ADDR_WIDTH(AXI_ADDR_WIDTH),
.DMA_2D_TRANSFER(1'b0),
.ASYNC_CLK_REQ_SRC(1),
.ASYNC_CLK_SRC_DEST(1),
.ASYNC_CLK_DEST_REQ(0),
.AXI_SLICE_DEST(1),
.AXI_SLICE_SRC(1),
.MAX_BYTES_PER_BURST(MAX_BYTES_PER_BURST_LMT),
.FIFO_SIZE(DST_FIFO_SIZE),
.ID_WIDTH($clog2(DST_FIFO_SIZE)),
.AXI_LENGTH_WIDTH_SRC(8-(4*AXI_PROTOCOL)),
.AXI_LENGTH_WIDTH_DEST(8-(4*AXI_PROTOCOL)),
.ENABLE_DIAGNOSTICS_IF(0),
.ALLOW_ASYM_MEM(1)
) i_rd_transfer (
.ctrl_clk(m_axis_aclk),
.ctrl_resetn(m_axis_aresetn),
// Control interface
.ctrl_enable(rd_request_enable),
.ctrl_pause(1'b0),
.req_valid(rd_request_valid),
.req_ready(rd_request_ready_loc[i]),
.req_dest_address(0),
.req_src_address(ADDR_OFFSET[AXI_ADDR_WIDTH-1:AXI_BYTES_PER_BEAT_WIDTH]),
.req_x_length(rd_request_length >> NUM_M_LOG2),
.req_y_length(0),
.req_dest_stride(0),
.req_src_stride(0),
.req_sync_transfer_start(1'b0),
.req_last(1'b1),
.req_eot(rd_request_eot_loc[i]),
.req_measured_burst_length(),
.req_response_partial(),
.req_response_valid(rd_response_valid_loc[i]),
.req_response_ready(rd_response_ready_loc[i]),
.m_dest_axi_aclk(1'b0),
.m_dest_axi_aresetn(1'b0),
.m_src_axi_aclk(m_axi_aclk),
.m_src_axi_aresetn(m_axi_aresetn),
.m_axi_awaddr(),
.m_axi_awlen(),
.m_axi_awsize(),
.m_axi_awburst(),
.m_axi_awprot(),
.m_axi_awcache(),
.m_axi_awvalid(),
.m_axi_awready(1'b1),
.m_axi_wdata(),
.m_axi_wstrb(),
.m_axi_wready(1'b1),
.m_axi_wvalid(),
.m_axi_wlast(),
.m_axi_bvalid(1'b0),
.m_axi_bresp(),
.m_axi_bready(),
.m_axi_arready(m_axi_arready[i]),
.m_axi_arvalid(m_axi_arvalid[i]),
.m_axi_araddr(m_axi_araddr[AXI_ADDR_WIDTH*i+:AXI_ADDR_WIDTH]),
.m_axi_arlen(m_axi_arlen[AXI_ALEN*i+:AXI_ALEN]),
.m_axi_arsize(m_axi_arsize[3*i+:3]),
.m_axi_arburst(m_axi_arburst[2*i+:2]),
.m_axi_arprot(),
.m_axi_arcache(),
.m_axi_rdata(m_axi_rdata[AXI_DATA_WIDTH*i+:AXI_DATA_WIDTH]),
.m_axi_rready(m_axi_rready[i]),
.m_axi_rvalid(m_axi_rvalid[i]),
.m_axi_rlast(m_axi_rlast[i]),
.m_axi_rresp(m_axi_rresp[2*i+:2]),
.s_axis_aclk(1'b0),
.s_axis_ready(),
.s_axis_valid(1'b0),
.s_axis_data(),
.s_axis_user(),
.s_axis_last(),
.s_axis_xfer_req(),
.m_axis_aclk(m_axis_aclk),
.m_axis_ready((m_axis_ready & m_axis_valid) | rd_needs_reset),
.m_axis_valid(m_axis_valid_loc[i]),
.m_axis_data(m_axis_data[DST_DATA_WIDTH_PER_M*i+:DST_DATA_WIDTH_PER_M]),
.m_axis_last(m_axis_last_loc[i]),
.m_axis_xfer_req(m_axis_xfer_req),
.fifo_wr_clk(1'b0),
.fifo_wr_en(1'b0),
.fifo_wr_din('b0),
.fifo_wr_overflow(),
.fifo_wr_sync(),
.fifo_wr_xfer_req(),
.fifo_rd_clk(1'b0),
.fifo_rd_en(1'b0),
.fifo_rd_valid(),
.fifo_rd_dout(),
.fifo_rd_underflow(),
.fifo_rd_xfer_req(),
// DBG
.dbg_dest_request_id(),
.dbg_dest_address_id(),
.dbg_dest_data_id(),
.dbg_dest_response_id(),
.dbg_src_request_id(),
.dbg_src_address_id(),
.dbg_src_data_id(),
.dbg_src_response_id(),
.dbg_status(rd_dbg_status),
.dest_diag_level_bursts()
);
assign rd_needs_reset = rd_dbg_status[11];
end
endgenerate
assign wr_overflow = |wr_overflow_loc;
assign rd_underflow = |rd_underflow_loc;
endmodule

View File

@ -0,0 +1,298 @@
set_property ASYNC_REG TRUE \
[get_cells -quiet -hier *cdc_sync_stage1_reg*] \
[get_cells -quiet -hier *cdc_sync_stage2_reg*]
# i_wr_transfer constraints
# .async_req_src(0),
# .async_src_dest(1),
# .async_dest_req(1),
set req_clk [get_clocks -of_objects [get_ports s_axis_aclk]]
set src_clk [get_clocks -of_objects [get_ports s_axis_aclk]]
set dest_clk [get_clocks -of_objects [get_ports m_axi_aclk]]
set_max_delay -quiet -datapath_only \
-from $dest_clk \
-to [get_cells -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_wr_transfer*i_sync_req_response_id* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $dest_clk]
set_false_path -quiet \
-from $dest_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_wr_transfer*i_sync_status_dest* && IS_SEQUENTIAL}]
set_false_path -quiet \
-from $req_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_wr_transfer*i_sync_control_dest* && IS_SEQUENTIAL}]
set_max_delay -quiet -datapath_only \
-from $dest_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_wr_transfer*i_dest_response_fifo/zerodeep.i_waddr_sync* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $dest_clk]
set_max_delay -quiet -datapath_only \
-from $req_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_wr_transfer*i_dest_response_fifo/zerodeep.i_raddr_sync* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $req_clk]
set_max_delay -quiet -datapath_only \
-from [get_cells -quiet -hier *cdc_sync_fifo_ram_reg* \
-filter {NAME =~ *i_wr_transfer*i_dest_response_fifo* && IS_SEQUENTIAL}] \
-to $req_clk \
[get_property -min PERIOD $req_clk]
set_max_delay -quiet -datapath_only \
-from $src_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_wr_transfer*i_sync_dest_request_id* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $src_clk]
set_max_delay -quiet -datapath_only \
-from $src_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_wr_transfer*i_store_and_forward/i_dest_sync_id* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $src_clk]
set_max_delay -quiet -datapath_only \
-from $dest_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_wr_transfer*i_store_and_forward/i_src_sync_id* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $dest_clk]
set_max_delay -quiet -datapath_only \
-from $src_clk \
-through [get_cells -quiet -hier \
-filter {IS_SEQUENTIAL && NAME =~ *i_wr_transfer*i_store_and_forward/burst_len_mem_reg*}] \
-to $dest_clk \
[get_property -min PERIOD $dest_clk]
set_max_delay -quiet -datapath_only \
-from $src_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_wr_transfer*i_dest_req_fifo/zerodeep.i_waddr_sync* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $src_clk]
set_max_delay -quiet -datapath_only \
-from $dest_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_wr_transfer*i_dest_req_fifo/zerodeep.i_raddr_sync* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $dest_clk]
set_max_delay -quiet -datapath_only \
-from [get_cells -quiet -hier *cdc_sync_fifo_ram_reg* \
-filter {NAME =~ *i_wr_transfer*i_dest_req_fifo* && IS_SEQUENTIAL}] \
-to $dest_clk \
[get_property -min PERIOD $dest_clk]
set_max_delay -quiet -datapath_only \
-from $src_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_wr_transfer*i_src_dest_bl_fifo/zerodeep.i_waddr_sync* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $src_clk]
set_max_delay -quiet -datapath_only \
-from $dest_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_wr_transfer*i_src_dest_bl_fifo/zerodeep.i_raddr_sync* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $dest_clk]
set_max_delay -quiet -datapath_only \
-from [get_cells -quiet -hier *cdc_sync_fifo_ram_reg* \
-filter {NAME =~ *i_wr_transfer*i_src_dest_bl_fifo* && IS_SEQUENTIAL}] \
-to $dest_clk \
[get_property -min PERIOD $dest_clk]
set_max_delay -quiet -datapath_only \
-from $src_clk \
-through [get_cells -quiet -hier DP \
-filter {NAME =~ *i_wr_transfer*i_request_arb/eot_mem_dest_reg*}] \
-to $dest_clk \
[get_property -min PERIOD $dest_clk]
# i_rd_transfer constraints
# .async_req_src(1),
# .async_src_dest(1),
# .async_dest_req(0),
set req_clk [get_clocks -of_objects [get_ports m_axis_aclk]]
set src_clk [get_clocks -of_objects [get_ports m_axi_aclk]]
set dest_clk [get_clocks -of_objects [get_ports m_axis_aclk]]
set_max_delay -quiet -datapath_only \
-from $req_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_rd_transfer*i_sync_src_request_id* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $req_clk]
set_false_path -quiet \
-from $src_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_rd_transfer*i_sync_status_src* && IS_SEQUENTIAL}]
set_false_path -quiet \
-from $req_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_rd_transfer*i_sync_control_src* && IS_SEQUENTIAL}]
set_max_delay -quiet -datapath_only \
-from $req_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_rd_transfer*i_src_req_fifo/zerodeep.i_waddr_sync* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $req_clk]
set_max_delay -quiet -datapath_only \
-from $src_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_rd_transfer*i_src_req_fifo/zerodeep.i_raddr_sync* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $src_clk]
set_max_delay -quiet -datapath_only \
-from [get_cells -quiet -hier *cdc_sync_fifo_ram_reg* \
-filter {NAME =~ *i_rd_transfer*i_src_req_fifo* && IS_SEQUENTIAL}] \
-to $src_clk \
[get_property -min PERIOD $src_clk]
set_max_delay -quiet -datapath_only \
-from $req_clk \
-through [get_cells -quiet -hier DP \
-filter {NAME =~ *i_rd_transfer*i_request_arb/eot_mem_src_reg*}] \
-to $src_clk \
[get_property -min PERIOD $src_clk]
set_max_delay -quiet -datapath_only \
-from $src_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_rd_transfer*i_rewind_req_fifo/zerodeep.i_waddr_sync* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $src_clk]
set_max_delay -quiet -datapath_only \
-from $req_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_rd_transfer*i_rewind_req_fifo/zerodeep.i_raddr_sync* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $req_clk]
set_max_delay -quiet -datapath_only \
-from [get_cells -quiet -hier *cdc_sync_fifo_ram_reg* \
-filter {NAME =~ *i_rd_transfer*i_rewind_req_fifo* && IS_SEQUENTIAL}] \
-to $req_clk \
[get_property -min PERIOD $req_clk]
set_false_path -quiet \
-from $req_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_rd_transfer*sync_rewind/i_sync_out* && IS_SEQUENTIAL}]
set_false_path -quiet \
-from $src_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_rd_transfer*sync_rewind/i_sync_in* && IS_SEQUENTIAL}]
set_max_delay -quiet -datapath_only \
-from $src_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_rd_transfer*i_sync_dest_request_id* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $src_clk]
set_max_delay -quiet -datapath_only \
-from $src_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_rd_transfer*i_store_and_forward/i_dest_sync_id* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $src_clk]
set_max_delay -quiet -datapath_only \
-from $dest_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_rd_transfer*i_store_and_forward/i_src_sync_id* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $dest_clk]
set_max_delay -quiet -datapath_only \
-from $src_clk \
-through [get_cells -quiet -hier \
-filter {IS_SEQUENTIAL && NAME =~ *i_rd_transfer*i_store_and_forward/burst_len_mem_reg*}] \
-to $dest_clk \
[get_property -min PERIOD $dest_clk]
set_max_delay -quiet -datapath_only \
-from $src_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_rd_transfer*i_dest_req_fifo/zerodeep.i_waddr_sync* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $src_clk]
set_max_delay -quiet -datapath_only \
-from $dest_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_rd_transfer*i_dest_req_fifo/zerodeep.i_raddr_sync* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $dest_clk]
set_max_delay -quiet -datapath_only \
-from [get_cells -quiet -hier *cdc_sync_fifo_ram_reg* \
-filter {NAME =~ *i_rd_transfer*i_dest_req_fifo* && IS_SEQUENTIAL}] \
-to $dest_clk \
[get_property -min PERIOD $dest_clk]
set_max_delay -quiet -datapath_only \
-from $src_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_rd_transfer*i_src_dest_bl_fifo/zerodeep.i_waddr_sync* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $src_clk]
set_max_delay -quiet -datapath_only \
-from $dest_clk \
-to [get_cells -quiet -hier *cdc_sync_stage1_reg* \
-filter {NAME =~ *i_rd_transfer*i_src_dest_bl_fifo/zerodeep.i_raddr_sync* && IS_SEQUENTIAL}] \
[get_property -min PERIOD $dest_clk]
set_max_delay -quiet -datapath_only \
-from [get_cells -quiet -hier *cdc_sync_fifo_ram_reg* \
-filter {NAME =~ *i_rd_transfer*i_src_dest_bl_fifo* && IS_SEQUENTIAL}] \
-to $dest_clk \
[get_property -min PERIOD $dest_clk]
set_max_delay -quiet -datapath_only \
-from $src_clk \
-through [get_cells -quiet -hier DP \
-filter {NAME =~ *i_rd_transfer*i_request_arb/eot_mem_dest_reg*}] \
-to $dest_clk \
[get_property -min PERIOD $dest_clk]
#
# Common to both dmas
#
# Reset signals
set_false_path -quiet \
-from [get_cells -quiet -hier *do_reset_reg* \
-filter {NAME =~ *i_reset_manager* && IS_SEQUENTIAL}] \
-to [get_pins -quiet -hier *reset_async_reg*/PRE]
set_false_path -quiet \
-from [get_cells -quiet -hier *reset_async_reg[0] \
-filter {NAME =~ *i_reset_manager* && IS_SEQUENTIAL}] \
-to [get_cells -quiet -hier *reset_async_reg[3]* \
-filter {NAME =~ *i_reset_manager* && IS_SEQUENTIAL}]
set_false_path -quiet \
-from [get_cells -quiet -hier *reset_async_reg[0] \
-filter {NAME =~ *i_reset_manager* && IS_SEQUENTIAL}] \
-to [get_pins -quiet -hier *reset_sync_in_reg*/PRE \
-filter {NAME =~ *i_reset_manager*}]
set_false_path -quiet \
-from [get_cells -quiet -hier *reset_sync_reg[0] \
-filter {NAME =~ *i_reset_manager* && IS_SEQUENTIAL}] \
-to [get_pins -quiet -hier *reset_sync_in_reg*/PRE \
-filter {NAME =~ *i_reset_manager*}]
set_property -dict { \
SHREG_EXTRACT NO \
ASYNC_REG TRUE \
} [get_cells -quiet -hier *reset_async_reg*]
set_property -dict { \
SHREG_EXTRACT NO \
ASYNC_REG TRUE \
} [get_cells -quiet -hier *reset_sync_reg*]

View File

@ -0,0 +1,353 @@
source ../scripts/adi_env.tcl
source $ad_hdl_dir/library/scripts/adi_ip_xilinx.tcl
adi_ip_create util_hbm
adi_ip_files util_hbm [list \
"util_hbm_constr.xdc" \
"util_hbm_ooc.ttcl" \
"util_hbm.v" \
"bd/bd.tcl" \
]
adi_ip_properties_lite util_hbm
adi_ip_ttcl util_dacfifo "util_hbm_ooc.ttcl"
adi_ip_bd util_hbm "bd/bd.tcl"
set_property PROCESSING_ORDER LATE [ipx::get_files util_hbm_constr.xdc \
-of_objects [ipx::get_file_groups -of_objects [ipx::current_core] \
-filter {NAME =~ *synthesis*}]]
adi_ip_add_core_dependencies { \
analog.com:user:util_cdc:1.0 \
analog.com:user:axi_dmac:1.0 \
}
set_property display_name "ADI AXIS to HBM/DDR AXI bridge" [ipx::current_core]
set_property description "Bridge between a AXI Stream master/slave interface and an AXI Memory Mapped interface" [ipx::current_core]
set max_axi_ifc 16
set cc [ipx::current_core]
adi_add_bus "s_axis" "slave" \
"xilinx.com:interface:axis_rtl:1.0" \
"xilinx.com:interface:axis:1.0" \
[list {"s_axis_ready" "TREADY"} \
{"s_axis_valid" "TVALID"} \
{"s_axis_data" "TDATA"} \
{"s_axis_strb" "TSTRB"} \
{"s_axis_keep" "TKEEP"} \
{"s_axis_user" "TUSER"} \
{"s_axis_last" "TLAST"}]
adi_add_bus "m_axis" "master" \
"xilinx.com:interface:axis_rtl:1.0" \
"xilinx.com:interface:axis:1.0" \
[list {"m_axis_ready" "TREADY"} \
{"m_axis_valid" "TVALID"} \
{"m_axis_data" "TDATA"} \
{"m_axis_strb" "TSTRB"} \
{"m_axis_keep" "TKEEP"} \
{"m_axis_user" "TUSER"} \
{"m_axis_last" "TLAST"}]
adi_add_multi_bus $max_axi_ifc "MAXI_" "master" \
"xilinx.com:interface:aximm_rtl:1.0" \
"xilinx.com:interface:aximm:1.0" \
[list \
{ "m_axi_araddr" "ARADDR" 32 "(spirit:decode(id('MODELPARAM_VALUE.AXI_ADDR_WIDTH')))"} \
{ "m_axi_arburst" "ARBURST" 2 } \
{ "m_axi_arlen" "ARLEN" 4 "8-(spirit:decode(id('MODELPARAM_VALUE.AXI_PROTOCOL')) * 4)"} \
{ "m_axi_arready" "ARREADY" 1 } \
{ "m_axi_arsize" "ARSIZE" 3 } \
{ "m_axi_arvalid" "ARVALID" 1 } \
{ "m_axi_awaddr" "AWADDR" 32 "(spirit:decode(id('MODELPARAM_VALUE.AXI_ADDR_WIDTH')) * 1)"} \
{ "m_axi_awburst" "AWBURST" 2 } \
{ "m_axi_awlen" "AWLEN" 4 "8-(spirit:decode(id('MODELPARAM_VALUE.AXI_PROTOCOL')) * 4)"} \
{ "m_axi_awready" "AWREADY" 1 } \
{ "m_axi_awsize" "AWSIZE" 3 } \
{ "m_axi_awvalid" "AWVALID" 1 } \
{ "m_axi_bready" "BREADY" 1 } \
{ "m_axi_bresp" "BRESP" 2 } \
{ "m_axi_bvalid" "BVALID" 1 } \
{ "m_axi_rdata" "RDATA" 32 "(spirit:decode(id('MODELPARAM_VALUE.AXI_DATA_WIDTH')))"} \
{ "m_axi_rlast" "RLAST" 1 } \
{ "m_axi_rready" "RREADY" 1 } \
{ "m_axi_rresp" "RRESP" 2 } \
{ "m_axi_rvalid" "RVALID" 1 } \
{ "m_axi_wdata" "WDATA" 32 "(spirit:decode(id('MODELPARAM_VALUE.AXI_DATA_WIDTH')))"} \
{ "m_axi_wlast" "WLAST" 1 } \
{ "m_axi_wready" "WREADY" 1 } \
{ "m_axi_wstrb" "WSTRB" 4 "(spirit:decode(id('MODELPARAM_VALUE.AXI_DATA_WIDTH'))/8)"} \
{ "m_axi_wvalid" "WVALID" 1 } \
] \
"(spirit:decode(id('MODELPARAM_VALUE.NUM_M')) > {i})"
set ifc_list ""
for {set idx 0} {$idx < $max_axi_ifc} {incr idx} {
set ifc_list $ifc_list:MAXI_$idx
ipx::add_address_space MAXI_$idx [ipx::current_core]
set_property master_address_space_ref MAXI_${idx} \
[ipx::get_bus_interfaces MAXI_$idx \
-of_objects [ipx::current_core]]
set_property range 4G [ipx::get_address_spaces MAXI_${idx}]
}
adi_add_bus_clock "m_axi_aclk" $ifc_list "m_axi_aresetn"
adi_add_bus "wr_ctrl" "slave" \
"analog.com:interface:if_do_ctrl_rtl:1.0" \
"analog.com:interface:if_do_ctrl:1.0" \
[list {"wr_request_enable" "request_enable"} \
{"wr_request_valid" "request_valid"} \
{"wr_request_ready" "request_ready"} \
{"wr_request_length" "request_length"} \
{"wr_response_measured_length" "response_measured_length"} \
{"wr_response_eot" "response_eot"} \
{"wr_overflow" "status_overflow"} \
]
adi_add_bus "rd_ctrl" "slave" \
"analog.com:interface:if_do_ctrl_rtl:1.0" \
"analog.com:interface:if_do_ctrl:1.0" \
[list {"rd_request_enable" "request_enable"} \
{"rd_request_valid" "request_valid"} \
{"rd_request_ready" "request_ready"} \
{"rd_request_length" "request_length"} \
{"rd_response_eot" "response_eot"} \
{"rd_underflow" "status_underflow"} \
]
adi_add_bus_clock "s_axis_aclk" "s_axis:wr_ctrl" "s_axis_aresetn"
adi_add_bus_clock "m_axis_aclk" "m_axis:rd_ctrl" "m_axis_aresetn"
# The core does not issue narrow bursts
foreach intf [ipx::get_bus_interfaces MAXI_* -of_objects $cc] {
set para [ipx::add_bus_parameter SUPPORTS_NARROW_BURST $intf]
set_property "VALUE" "0" $para
}
#
# Parameters description
#
set_property -dict [list \
"value_resolve_type" "user" \
"value" 1 \
"value_validation_type" "pairs" \
"value_validation_pairs" {"TX (DAC)" 1 "RX (ADC)" 0} \
] \
[ipx::get_user_parameters TX_RX_N -of_objects $cc]
foreach dir {"SRC" "DST"} {
set_property -dict [list \
"value_validation_type" "list" \
"value_validation_list" "16 32 64 128 256 512 1024 2048 4096" \
] \
[ipx::get_user_parameters ${dir}_DATA_WIDTH -of_objects $cc]
}
set_property -dict [list \
"value_validation_type" "pairs" \
"value" "24" \
"value_validation_pairs" {\
"256MB" "28" \
"512MB" "29" \
"1GB" "30" \
"2GB" "31" \
"4GB" "32" \
"8GB" "33" \
"16GB" "34" \
} \
] \
[ipx::get_user_parameters LENGTH_WIDTH -of_objects $cc]
set_property -dict [list \
"value_resolve_type" "user" \
"value" 2 \
"value_validation_type" "pairs" \
"value_validation_pairs" {"HBM" 2 "DDR" 1} \
] \
[ipx::get_user_parameters MEM_TYPE -of_objects $cc]
set_property -dict [list \
"value_validation_type" "pairs" \
"value" "0" \
"value_validation_pairs" {"AXI3" "1" "AXI4" "0"} \
] \
[ipx::get_user_parameters AXI_PROTOCOL -of_objects $cc]
set_property -dict [list \
"value_validation_type" "list" \
"value_validation_list" "32 64 128 256 512 1024" \
] \
[ipx::get_user_parameters AXI_DATA_WIDTH -of_objects $cc]
set_property -dict [list \
"value_validation_type" "list" \
"value_validation_list" "1 2 4 8 16" \
] \
[ipx::get_user_parameters NUM_M -of_objects $cc]
set_property -dict [list \
"value_validation_type" "list" \
"value_validation_list" "2 4 8 16 32" \
] \
[ipx::get_user_parameters SRC_FIFO_SIZE -of_objects $cc]
set_property -dict [list \
"value_validation_type" "list" \
"value_validation_list" "2 4 8 16 32" \
] \
[ipx::get_user_parameters DST_FIFO_SIZE -of_objects $cc]
# 1 segment = 256MB
# HBM_SEGMENTS_PER_MASTER = Storage size (MB) / 256 (MB) / number of masters
set_property -dict [list \
"enablement_value" "false" \
"value_tcl_expr" {expr int(ceil(2**($LENGTH_WIDTH-28) / ${NUM_M}.0)) } \
] \
[ipx::get_user_parameters HBM_SEGMENTS_PER_MASTER -of_objects $cc]
set_property -dict [list \
"value_validation_type" "range_long" \
"value_validation_range_minimum" "0" \
"value_validation_range_maximum" "15" \
"enablement_tcl_expr" "\$MEM_TYPE == 2" \
] \
[ipx::get_user_parameters HBM_SEGMENT_INDEX -of_objects $cc]
set_property -dict [list \
"value_validation_type" "range_long" \
"value_validation_range_minimum" "0" \
"value_validation_range_maximum" "4294967296" \
"enablement_tcl_expr" "\$MEM_TYPE == 1" \
] \
[ipx::get_user_parameters DDR_BASE_ADDDRESS -of_objects $cc]
#
# GUI formatting
#
set page0 [ipgui::get_pagespec -name "Page 0" -component $cc]
# General settings group
set group [ipgui::add_group -name "General Settings" -component $cc \
-parent $page0 -display_name "General Settings"]
set p [ipgui::get_guiparamspec -name "TX_RX_N" -component $cc]
ipgui::move_param -component $cc -order 0 $p -parent $group
set_property -dict [list \
"display_name" "Datapath type" \
"widget" "comboBox" \
] $p
set p [ipgui::get_guiparamspec -name "LENGTH_WIDTH" -component $cc]
ipgui::move_param -component $cc -order 1 $p -parent $group
set_property -dict [list \
"display_name" "Storage size" \
"tooltip" "Defines the amount of data can be stored starting from the base address of the storage."\
] $p
# Offload interface group
set group [ipgui::add_group -name "Data Offload Interface" -component $cc \
-parent $page0 -display_name "Data Offload Interface"]
set p [ipgui::get_guiparamspec -name "SRC_DATA_WIDTH" -component $cc]
ipgui::move_param -component $cc -order 0 $p -parent $group
set_property -dict [list \
"display_name" "Source AXIS Bus Width" \
"tooltip" "Source AXIS Bus Width (s_axis)" \
] $p
set p [ipgui::get_guiparamspec -name "SRC_FIFO_SIZE" -component $cc]
ipgui::move_param -component $cc -order 1 $p -parent $group
set_property -dict [list \
"display_name" "Write Buffer Size" \
"tooltip" "Size of internal data mover buffer used for write to external memory. In AXI bursts, where one burst is max 4096 bytes or 2*AXI_DATA_WIDTH bytes for AXI3 or 32*AXI_DATA_WIDTH bytes for AXI4." \
"widget" "comboBox" \
] $p
set p [ipgui::get_guiparamspec -name "DST_DATA_WIDTH" -component $cc]
ipgui::move_param -component $cc -order 2 $p -parent $group
set_property -dict [list \
"display_name" "Destination AXIS Bus Width" \
"tooltip" "Destination AXIS Bus Width (m_axis)" \
] $p
set p [ipgui::get_guiparamspec -name "DST_FIFO_SIZE" -component $cc]
ipgui::move_param -component $cc -order 3 $p -parent $group
set_property -dict [list \
"display_name" "Read Buffer Size" \
"tooltip" "Size of internal data mover buffer used for read from the external memory. In AXI bursts, where one burst is max 4096 bytes or 2*AXI_DATA_WIDTH bytes for AXI3 or 32*AXI_DATA_WIDTH bytes for AXI4." \
"widget" "comboBox" \
] $p
# Memory interface group
set group [ipgui::add_group -name "External Memory Interface" -component $cc \
-parent $page0 -display_name "External Memory Interface"]
set p [ipgui::get_guiparamspec -name "MEM_TYPE" -component $cc]
ipgui::move_param -component $cc -order 0 $p -parent $group
set_property -dict [list \
"display_name" "External Storage Type" \
"tooltip" "External Storage Type" \
"widget" "comboBox" \
] $p
set p [ipgui::get_guiparamspec -name "NUM_M" -component $cc]
ipgui::move_param -component $cc -order 1 $p -parent $group
set_property -dict [list \
"display_name" "Number of AXI Masters" \
"tooltip" "Number of AXI masters the data stream bus is split" \
"widget" "comboBox" \
] $p
set p [ipgui::get_guiparamspec -name "AXI_PROTOCOL" -component $cc]
ipgui::move_param -component $cc -order 2 $p -parent $group
set_property -dict [list \
"widget" "comboBox" \
"display_name" "AXI Protocol" \
] $p
set p [ipgui::get_guiparamspec -name "AXI_DATA_WIDTH" -component $cc]
ipgui::move_param -component $cc -order 3 $p -parent $group
set_property -dict [list \
"display_name" "AXI Data Bus Width" \
"tooltip" "Bus Width: Memory-Mapped interface with valid range of 32-1024 bits" \
] $p
# HBM sub group
set hbm_group [ipgui::add_group -name "HBM Interface" -component $cc \
-parent $group -display_name "HBM Interface"]
set p [ipgui::get_guiparamspec -name "HBM_SEGMENTS_PER_MASTER" -component $cc]
ipgui::move_param -component $cc -order 0 $p -parent $hbm_group
set_property -dict [list \
"widget" {textEdit} \
"display_name" "HBM sections per master" \
"tooltip" "HBM sections (2Gb/256MB pseudo channels) per master" \
] $p
set p [ipgui::get_guiparamspec -name "HBM_SEGMENT_INDEX" -component $cc]
ipgui::move_param -component $cc -order 1 $p -parent $hbm_group
set_property -dict [list \
"display_name" "First HBM section index" \
"tooltip" "First used HBM section (2Gb pseudo channels).The base address where data is stored is generated based on this parameter" \
"widget" "comboBox" \
] $p
# DDR sub group
set hbm_group [ipgui::add_group -name "DDR Interface" -component $cc \
-parent $group -display_name "DDR Interface"]
set p [ipgui::get_guiparamspec -name "DDR_BASE_ADDDRESS" -component $cc]
ipgui::move_param -component $cc -order 0 $p -parent $hbm_group
set_property -dict [list \
"display_name" "DDR base address" \
"tooltip" "The base address where data is stored is generated based on this parameter" \
] $p
ipgui::remove_param -component $cc [ipgui::get_guiparamspec -name "AXI_ADDR_WIDTH" -component $cc]
ipx::create_xgui_files [ipx::current_core]
ipx::save_core [ipx::current_core]

View File

@ -0,0 +1,15 @@
<: setFileUsedIn { out_of_context synthesis implementation } :>
<: ;#Component and file information :>
<: set ComponentName [getComponentNameString] :>
<: setOutputDirectory "./" :>
<: setFileName $ComponentName :>
<: setFileExtension "_ooc.xdc" :>
# This XDC is used only for OOC mode of synthesis, implementation.
# These are default values for timing driven synthesis during OOC flow.
# These values will be overwritten during implementation with information
# from top level.
create_clock -name s_axis_aclk -period 2.5 [get_ports s_axis_aclk]
create_clock -name m_axis_aclk -period 2.5 [get_ports m_axis_aclk]
create_clock -name m_axi_aclk -period 2.5 [get_ports m_axi_aclk]