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]