From 3bf7b6c80f7fde085526c8a9732b2e47ad27410c Mon Sep 17 00:00:00 2001 From: Laszlo Nagy Date: Mon, 14 Feb 2022 15:13:14 +0000 Subject: [PATCH] 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. --- library/util_hbm/Makefile | 21 + library/util_hbm/bd/bd.tcl | 86 ++++ library/util_hbm/scripts/adi_util_hbm.tcl | 117 +++++ library/util_hbm/util_hbm.v | 574 ++++++++++++++++++++++ library/util_hbm/util_hbm_constr.xdc | 298 +++++++++++ library/util_hbm/util_hbm_ip.tcl | 353 +++++++++++++ library/util_hbm/util_hbm_ooc.ttcl | 15 + 7 files changed, 1464 insertions(+) create mode 100644 library/util_hbm/Makefile create mode 100644 library/util_hbm/bd/bd.tcl create mode 100644 library/util_hbm/scripts/adi_util_hbm.tcl create mode 100644 library/util_hbm/util_hbm.v create mode 100644 library/util_hbm/util_hbm_constr.xdc create mode 100644 library/util_hbm/util_hbm_ip.tcl create mode 100644 library/util_hbm/util_hbm_ooc.ttcl diff --git a/library/util_hbm/Makefile b/library/util_hbm/Makefile new file mode 100644 index 000000000..cbc6c9336 --- /dev/null +++ b/library/util_hbm/Makefile @@ -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 diff --git a/library/util_hbm/bd/bd.tcl b/library/util_hbm/bd/bd.tcl new file mode 100644 index 000000000..8ad164074 --- /dev/null +++ b/library/util_hbm/bd/bd.tcl @@ -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" + +} diff --git a/library/util_hbm/scripts/adi_util_hbm.tcl b/library/util_hbm/scripts/adi_util_hbm.tcl new file mode 100644 index 000000000..8ac03119c --- /dev/null +++ b/library/util_hbm/scripts/adi_util_hbm.tcl @@ -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]]]] +} diff --git a/library/util_hbm/util_hbm.v b/library/util_hbm/util_hbm.v new file mode 100644 index 000000000..86accffb3 --- /dev/null +++ b/library/util_hbm/util_hbm.v @@ -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: +// +// +// 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 + diff --git a/library/util_hbm/util_hbm_constr.xdc b/library/util_hbm/util_hbm_constr.xdc new file mode 100644 index 000000000..4ff59d373 --- /dev/null +++ b/library/util_hbm/util_hbm_constr.xdc @@ -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*] + diff --git a/library/util_hbm/util_hbm_ip.tcl b/library/util_hbm/util_hbm_ip.tcl new file mode 100644 index 000000000..98a6659a8 --- /dev/null +++ b/library/util_hbm/util_hbm_ip.tcl @@ -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] diff --git a/library/util_hbm/util_hbm_ooc.ttcl b/library/util_hbm/util_hbm_ooc.ttcl new file mode 100644 index 000000000..bb69760a0 --- /dev/null +++ b/library/util_hbm/util_hbm_ooc.ttcl @@ -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]