diff --git a/library/Makefile b/library/Makefile index d98dc2924c9..83a3bef32bc 100644 --- a/library/Makefile +++ b/library/Makefile @@ -49,6 +49,7 @@ clean: $(MAKE) -C axi_intr_monitor clean $(MAKE) -C axi_laser_driver clean $(MAKE) -C axi_logic_analyzer clean + $(MAKE) -C axi_ltc235x clean $(MAKE) -C axi_ltc2387 clean $(MAKE) -C axi_pulse_gen clean $(MAKE) -C axi_pwm_gen clean @@ -171,6 +172,7 @@ lib: $(MAKE) -C axi_intr_monitor $(MAKE) -C axi_laser_driver $(MAKE) -C axi_logic_analyzer + $(MAKE) -C axi_ltc235x $(MAKE) -C axi_ltc2387 $(MAKE) -C axi_pulse_gen $(MAKE) -C axi_pwm_gen diff --git a/library/axi_ltc235x/Makefile b/library/axi_ltc235x/Makefile new file mode 100644 index 00000000000..f1d9b3ebb30 --- /dev/null +++ b/library/axi_ltc235x/Makefile @@ -0,0 +1,26 @@ +#################################################################################### +## Copyright (c) 2018 - 2023 Analog Devices, Inc. +### SPDX short identifier: BSD-1-Clause +## Auto-generated, do not modify! +#################################################################################### + +LIBRARY_NAME := axi_ltc235x + +GENERIC_DEPS += ../common/ad_rst.v +GENERIC_DEPS += ../common/up_adc_channel.v +GENERIC_DEPS += ../common/up_adc_common.v +GENERIC_DEPS += ../common/up_axi.v +GENERIC_DEPS += ../common/up_clock_mon.v +GENERIC_DEPS += ../common/up_xfer_cntrl.v +GENERIC_DEPS += ../common/up_xfer_status.v +GENERIC_DEPS += axi_ltc235x.v +GENERIC_DEPS += axi_ltc235x_cmos.v +GENERIC_DEPS += axi_ltc235x_lvds.v + +INTEL_DEPS += ../intel/common/up_clock_mon_constr.sdc +INTEL_DEPS += ../intel/common/up_rst_constr.sdc +INTEL_DEPS += ../intel/common/up_xfer_cntrl_constr.sdc +INTEL_DEPS += ../intel/common/up_xfer_status_constr.sdc +INTEL_DEPS += axi_ltc235x_hw.tcl + +include ../scripts/library.mk diff --git a/library/axi_ltc235x/axi_ltc235x.v b/library/axi_ltc235x/axi_ltc235x.v new file mode 100644 index 00000000000..6c65c9d27a8 --- /dev/null +++ b/library/axi_ltc235x/axi_ltc235x.v @@ -0,0 +1,512 @@ +// *************************************************************************** +// *************************************************************************** +// Copyright 2023 (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. +// +// *************************************************************************** +// *************************************************************************** + +`timescale 1ns/100ps + +module axi_ltc235x #( + + parameter ID = 0, + parameter XILINX_INTEL_N = 0, + parameter FPGA_TECHNOLOGY = 0, + parameter FPGA_FAMILY = 0, + parameter SPEED_GRADE = 0, + parameter DEV_PACKAGE = 0, + parameter [0:0] LVDS_CMOS_N = 0, + parameter LANE_0_ENABLE = 1, + parameter LANE_1_ENABLE = 1, + parameter LANE_2_ENABLE = 1, + parameter LANE_3_ENABLE = 1, + parameter LANE_4_ENABLE = 1, + parameter LANE_5_ENABLE = 1, + parameter LANE_6_ENABLE = 1, + parameter LANE_7_ENABLE = 1, + parameter EXTERNAL_CLK = 0, + parameter LTC235X_FAMILY = 0, + parameter NUM_CHANNELS = 8, + parameter DATA_WIDTH = 18 +) ( + + // physical data interface + + input external_clk, + + // common + input busy, + output lvds_cmos_n, + + // cmos + output sdi, + output scki, + input scko, + input [ 7:0] sdo, + + // lvds + output sdi_p, + output sdi_n, + output scki_p, + output scki_n, + input scko_p, + input scko_n, + input sdo_p, + input sdo_n, + + // AXI Slave Memory Map + + input s_axi_aclk, + input s_axi_aresetn, + input s_axi_awvalid, + input [15:0] s_axi_awaddr, + input [ 2:0] s_axi_awprot, + output s_axi_awready, + input s_axi_wvalid, + input [31:0] s_axi_wdata, + input [ 3:0] s_axi_wstrb, + output s_axi_wready, + output s_axi_bvalid, + output [ 1:0] s_axi_bresp, + input s_axi_bready, + input s_axi_arvalid, + input [15:0] s_axi_araddr, + input [ 2:0] s_axi_arprot, + output s_axi_arready, + output s_axi_rvalid, + output [ 1:0] s_axi_rresp, + output [31:0] s_axi_rdata, + input s_axi_rready, + + // Write FIFO interface + + input adc_dovf, + output adc_valid_0, + output adc_valid_1, + output adc_valid_2, + output adc_valid_3, + output adc_valid_4, + output adc_valid_5, + output adc_valid_6, + output adc_valid_7, + output adc_enable_0, + output adc_enable_1, + output adc_enable_2, + output adc_enable_3, + output adc_enable_4, + output adc_enable_5, + output adc_enable_6, + output adc_enable_7, + output [31:0] adc_data_0, + output [31:0] adc_data_1, + output [31:0] adc_data_2, + output [31:0] adc_data_3, + output [31:0] adc_data_4, + output [31:0] adc_data_5, + output [31:0] adc_data_6, + output [31:0] adc_data_7 +); + + // localparam + + localparam [ 7:0] ACTIVE_LANES = { + LANE_7_ENABLE == 1 ? 1'b1 : 1'b0, + LANE_6_ENABLE == 1 ? 1'b1 : 1'b0, + LANE_5_ENABLE == 1 ? 1'b1 : 1'b0, + LANE_4_ENABLE == 1 ? 1'b1 : 1'b0, + LANE_3_ENABLE == 1 ? 1'b1 : 1'b0, + LANE_2_ENABLE == 1 ? 1'b1 : 1'b0, + LANE_1_ENABLE == 1 ? 1'b1 : 1'b0, + LANE_0_ENABLE == 1 ? 1'b1 : 1'b0}; + localparam [ 0:0] RD_RAW_DATA = 1'b1; + localparam [ 0:0] CMOS_OR_LVDS_N = ~LVDS_CMOS_N; + localparam [31:0] CONFIG = {RD_RAW_DATA, 5'b0, CMOS_OR_LVDS_N, 7'b0}; + + // internal registers + + reg up_wack = 1'b0; + reg up_rack = 1'b0; + reg [31:0] up_rdata = 32'b0; + + // internal signals + + wire [23:0] softspan_24; + wire [ 2:0] softspan_3[0:7]; + + wire adc_valid; + + wire up_clk; + wire up_rstn; + wire up_rreq_s; + wire [13:0] up_raddr_s; + wire up_wreq_s; + wire [13:0] up_waddr_s; + + wire adc_clk_s; + + wire [13:0] up_addr_s; + wire [31:0] up_wdata_s; + wire [31:0] up_rdata_s[0:8]; + wire [ 8:0] up_rack_s; + wire [ 8:0] up_wack_s; + + // read raw, feature + wire rd_req_s; + wire wr_req_s; + wire [15:0] wr_data_s; + wire [15:0] rd_data_s; + wire rd_valid_s; + + wire adc_rst_s; + + wire [ 7:0] adc_enable; + wire [ 2:0] adc_status_header[0:7]; + wire [31:0] adc_data[0:7]; + + // defaults + + assign up_clk = s_axi_aclk; + assign up_rstn = s_axi_aresetn; + + assign lvds_cmos_n = LVDS_CMOS_N[0]; + + assign adc_valid_0 = adc_valid; + assign adc_valid_1 = adc_valid; + assign adc_valid_2 = adc_valid; + assign adc_valid_3 = adc_valid; + assign adc_valid_4 = adc_valid; + assign adc_valid_5 = adc_valid; + assign adc_valid_6 = adc_valid; + assign adc_valid_7 = adc_valid; + + assign adc_enable_0 = adc_enable[0]; + assign adc_enable_1 = adc_enable[1]; + assign adc_enable_2 = adc_enable[2]; + assign adc_enable_3 = adc_enable[3]; + assign adc_enable_4 = adc_enable[4]; + assign adc_enable_5 = adc_enable[5]; + assign adc_enable_6 = adc_enable[6]; + assign adc_enable_7 = adc_enable[7]; + + assign adc_data_0 = adc_data[0]; + assign adc_data_1 = adc_data[1]; + assign adc_data_2 = adc_data[2]; + assign adc_data_3 = adc_data[3]; + assign adc_data_4 = adc_data[4]; + assign adc_data_5 = adc_data[5]; + assign adc_data_6 = adc_data[6]; + assign adc_data_7 = adc_data[7]; + + generate + if (NUM_CHANNELS == 8) begin + assign softspan_24 = {softspan_3[7], softspan_3[6], softspan_3[5], softspan_3[4], softspan_3[3], softspan_3[2], softspan_3[1], softspan_3[0]}; + end else if (NUM_CHANNELS == 4) begin + assign softspan_24 = {softspan_3[3], softspan_3[2], softspan_3[1], softspan_3[0], 12'b0}; + end else begin + assign softspan_24 = {softspan_3[1], softspan_3[0], 18'b0}; + end + endgenerate + + // processor read/write interface + + always @(negedge up_rstn or posedge up_clk) begin + if (up_rstn == 0) begin + up_wack <= 'd0; + up_rack <= 'd0; + up_rdata <= 'd0; + end else begin + up_wack <= |up_wack_s; + up_rack <= |up_rack_s; + up_rdata <= up_rdata_s[0] | + up_rdata_s[1] | + up_rdata_s[2] | + up_rdata_s[3] | + up_rdata_s[4] | + up_rdata_s[5] | + up_rdata_s[6] | + up_rdata_s[7] | + up_rdata_s[8]; + end + end + + // CMOS/LVDS INTERFACE + generate + if (EXTERNAL_CLK == 1'b1) begin + assign adc_clk_s = external_clk; + end else begin + assign adc_clk_s = up_clk; + end + + if (LVDS_CMOS_N == 1) begin + axi_ltc235x_lvds #( + .XILINX_INTEL_N (XILINX_INTEL_N), + .LTC235X_FAMILY (LTC235X_FAMILY), + .NUM_CHANNELS (NUM_CHANNELS), + .DATA_WIDTH (DATA_WIDTH) + ) i_ltc235x_lvds ( + .rst (adc_rst_s), + .clk (adc_clk_s), + .adc_enable (adc_enable), + .softspan_next (softspan_24), + .scki_p (scki_p), + .scki_n (scki_n), + .sdi_p (sdi_p), + .sdi_n (sdi_n), + .scko_p (scko_p), + .scko_n (scko_n), + .sdo_p (sdo_p), + .sdo_n (sdo_n), + .busy (busy), + .adc_ch0_id (adc_status_header[0]), + .adc_ch1_id (adc_status_header[1]), + .adc_ch2_id (adc_status_header[2]), + .adc_ch3_id (adc_status_header[3]), + .adc_ch4_id (adc_status_header[4]), + .adc_ch5_id (adc_status_header[5]), + .adc_ch6_id (adc_status_header[6]), + .adc_ch7_id (adc_status_header[7]), + .adc_data_0 (adc_data_0), + .adc_data_1 (adc_data_1), + .adc_data_2 (adc_data_2), + .adc_data_3 (adc_data_3), + .adc_data_4 (adc_data_4), + .adc_data_5 (adc_data_5), + .adc_data_6 (adc_data_6), + .adc_data_7 (adc_data_7), + .adc_softspan_0 (), + .adc_softspan_1 (), + .adc_softspan_2 (), + .adc_softspan_3 (), + .adc_softspan_4 (), + .adc_softspan_5 (), + .adc_softspan_6 (), + .adc_softspan_7 (), + .adc_valid (adc_valid)); + end else begin + axi_ltc235x_cmos #( + .LTC235X_FAMILY (LTC235X_FAMILY), + .ACTIVE_LANES (ACTIVE_LANES), + .NUM_CHANNELS (NUM_CHANNELS), + .DATA_WIDTH (DATA_WIDTH) + ) i_ltc235x_cmos ( + .rst (adc_rst_s), + .clk (adc_clk_s), + .adc_enable (adc_enable), + .softspan_next (softspan_24), + .scki (scki), + .sdi (sdi), + .scko (scko), + .sdo (sdo), + .busy (busy), + .adc_ch0_id (adc_status_header[0]), + .adc_ch1_id (adc_status_header[1]), + .adc_ch2_id (adc_status_header[2]), + .adc_ch3_id (adc_status_header[3]), + .adc_ch4_id (adc_status_header[4]), + .adc_ch5_id (adc_status_header[5]), + .adc_ch6_id (adc_status_header[6]), + .adc_ch7_id (adc_status_header[7]), + .adc_data_0 (adc_data_0), + .adc_data_1 (adc_data_1), + .adc_data_2 (adc_data_2), + .adc_data_3 (adc_data_3), + .adc_data_4 (adc_data_4), + .adc_data_5 (adc_data_5), + .adc_data_6 (adc_data_6), + .adc_data_7 (adc_data_7), + .adc_softspan_0 (), + .adc_softspan_1 (), + .adc_softspan_2 (), + .adc_softspan_3 (), + .adc_softspan_4 (), + .adc_softspan_5 (), + .adc_softspan_6 (), + .adc_softspan_7 (), + .adc_valid (adc_valid)); + end + endgenerate + + // regmap adc channels + + generate + genvar i; + for (i = 0; i < NUM_CHANNELS; i=i+1) begin : regmap_channels + up_adc_channel #( + .CHANNEL_ID(i) + ) i_up_adc_channel ( + .adc_clk (adc_clk_s), + .adc_rst (adc_rst_s), + .adc_enable (adc_enable[i]), + .adc_iqcor_enb (), + .adc_dcfilt_enb (), + .adc_dfmt_se (), + .adc_dfmt_type (), + .adc_dfmt_enable (), + .adc_dcfilt_offset (), + .adc_dcfilt_coeff (), + .adc_iqcor_coeff_1 (), + .adc_iqcor_coeff_2 (), + .adc_pnseq_sel (), + .adc_data_sel (), + .adc_pn_err (1'b0), + .adc_pn_oos (1'b0), + .adc_or (1'b0), + .adc_read_data (adc_data[i]), + .adc_status_header({5'd0, adc_status_header[i]}), + .adc_crc_err(1'b0), + .adc_softspan (softspan_3[i]), + .up_adc_crc_err (), + .up_adc_pn_err (), + .up_adc_pn_oos (), + .up_adc_or (), + .up_usr_datatype_be (), + .up_usr_datatype_signed (), + .up_usr_datatype_shift (), + .up_usr_datatype_total_bits (), + .up_usr_datatype_bits (), + .up_usr_decimation_m (), + .up_usr_decimation_n (), + .adc_usr_datatype_be (1'b0), + .adc_usr_datatype_signed (1'b1), + .adc_usr_datatype_shift (8'd0), + .adc_usr_datatype_total_bits (8'd32), + .adc_usr_datatype_bits (8'd32), + .adc_usr_decimation_m (16'd1), + .adc_usr_decimation_n (16'd1), + .up_rstn (up_rstn), + .up_clk (up_clk), + .up_wreq (up_wreq_s), + .up_waddr (up_waddr_s), + .up_wdata (up_wdata_s), + .up_wack (up_wack_s[i]), + .up_rreq (up_rreq_s), + .up_raddr (up_raddr_s), + .up_rdata (up_rdata_s[i]), + .up_rack (up_rack_s[i])); + end + endgenerate + + // up adc common + + up_adc_common #( + .ID(ID), + .CONFIG(CONFIG) + ) i_up_adc_common ( + .mmcm_rst (), + .adc_clk (adc_clk_s), + .adc_rst (adc_rst_s), + .adc_r1_mode (), + .adc_ddr_edgesel (), + .adc_pin_mode (), + .adc_status ('h1), + .adc_sync_status (1'b1), + .adc_status_ovf (adc_dovf), + .adc_clk_ratio (32'd1), + .adc_start_code (), + .adc_sref_sync (), + .adc_sync (), + .adc_ext_sync_arm(), + .adc_ext_sync_disarm(), + .adc_ext_sync_manual_req(), + .adc_num_lanes(), + .adc_custom_control(), + .adc_crc_enable(), + .adc_sdr_ddr_n(), + .adc_symb_op(), + .adc_symb_8_16b(), + .up_pps_rcounter (32'b0), + .up_pps_status (1'b0), + .up_pps_irq_mask (), + .up_adc_r1_mode(), + .up_adc_ce (), + .up_status_pn_err (1'b0), + .up_status_pn_oos (1'b0), + .up_status_or (1'b0), + .up_drp_sel (), + .up_drp_wr (), + .up_drp_addr (), + .up_drp_wdata (), + .up_drp_rdata (32'd0), + .up_drp_ready (1'd0), + .up_drp_locked (1'd1), + .adc_config_wr (), + .adc_config_ctrl (), + .adc_config_rd (32'd0), + .adc_ctrl_status (1'd0), + .up_usr_chanmax_out (), + .up_usr_chanmax_in (8), + .up_adc_gpio_in (32'b0), + .up_adc_gpio_out (), + .up_rstn (up_rstn), + .up_clk (up_clk), + .up_wreq (up_wreq_s), + .up_waddr (up_waddr_s), + .up_wdata (up_wdata_s), + .up_wack (up_wack_s[8]), + .up_rreq (up_rreq_s), + .up_raddr (up_raddr_s), + .up_rdata (up_rdata_s[8]), + .up_rack (up_rack_s[8])); + + // up bus interface + + up_axi #( + .AXI_ADDRESS_WIDTH (16) + ) i_up_axi ( + .up_rstn (up_rstn), + .up_clk (up_clk), + .up_axi_awvalid (s_axi_awvalid), + .up_axi_awaddr (s_axi_awaddr), + .up_axi_awready (s_axi_awready), + .up_axi_wvalid (s_axi_wvalid), + .up_axi_wdata (s_axi_wdata), + .up_axi_wstrb (s_axi_wstrb), + .up_axi_wready (s_axi_wready), + .up_axi_bvalid (s_axi_bvalid), + .up_axi_bresp (s_axi_bresp), + .up_axi_bready (s_axi_bready), + .up_axi_arvalid (s_axi_arvalid), + .up_axi_araddr (s_axi_araddr), + .up_axi_arready (s_axi_arready), + .up_axi_rvalid (s_axi_rvalid), + .up_axi_rresp (s_axi_rresp), + .up_axi_rdata (s_axi_rdata), + .up_axi_rready (s_axi_rready), + .up_wreq (up_wreq_s), + .up_waddr (up_waddr_s), + .up_wdata (up_wdata_s), + .up_wack (up_wack), + .up_rreq (up_rreq_s), + .up_raddr (up_raddr_s), + .up_rdata (up_rdata), + .up_rack (up_rack)); + +endmodule diff --git a/library/axi_ltc235x/axi_ltc235x_cmos.v b/library/axi_ltc235x/axi_ltc235x_cmos.v new file mode 100644 index 00000000000..5f0d1959e4e --- /dev/null +++ b/library/axi_ltc235x/axi_ltc235x_cmos.v @@ -0,0 +1,534 @@ +// *************************************************************************** +// *************************************************************************** +// Copyright 2023 (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. +// +// *************************************************************************** +// *************************************************************************** + +`timescale 1ns/100ps + +module axi_ltc235x_cmos #( + parameter ACTIVE_LANES = 8'b1111_1111, + parameter LTC235X_FAMILY = 0, + parameter NUM_CHANNELS = 8, // 8 for 2358, 4 for 2357, 2 for 2353 + parameter DATA_WIDTH = 18 // 18 or 16 based on -18/-16 +) ( + + input rst, + input clk, + input [ 7:0] adc_enable, + input [23:0] softspan_next, + + // physical interface + + output scki, + output sdi, + input scko, + input [ 7:0] sdo, + input busy, + + // FIFO interface + + output [ 2:0] adc_ch0_id, + output [ 2:0] adc_ch1_id, + output [ 2:0] adc_ch2_id, + output [ 2:0] adc_ch3_id, + output [ 2:0] adc_ch4_id, + output [ 2:0] adc_ch5_id, + output [ 2:0] adc_ch6_id, + output [ 2:0] adc_ch7_id, + + output [31:0] adc_data_0, + output [31:0] adc_data_1, + output [31:0] adc_data_2, + output [31:0] adc_data_3, + output [31:0] adc_data_4, + output [31:0] adc_data_5, + output [31:0] adc_data_6, + output [31:0] adc_data_7, + + output [ 2:0] adc_softspan_0, + output [ 2:0] adc_softspan_1, + output [ 2:0] adc_softspan_2, + output [ 2:0] adc_softspan_3, + output [ 2:0] adc_softspan_4, + output [ 2:0] adc_softspan_5, + output [ 2:0] adc_softspan_6, + output [ 2:0] adc_softspan_7, + + output reg adc_valid +); + + localparam DW = 24; // packet size + localparam BW = DW - 1; + + // internal registers + + reg busy_m1; + reg busy_m2; + reg busy_m3; + + reg [ 4:0] scki_counter = 5'h0; + reg [ 4:0] data_counter = 5'h0; + + reg scki_i; + reg scki_d; + + reg [BW:0] adc_lane_0; + reg [BW:0] adc_lane_1; + reg [BW:0] adc_lane_2; + reg [BW:0] adc_lane_3; + reg [BW:0] adc_lane_4; + reg [BW:0] adc_lane_5; + reg [BW:0] adc_lane_6; + reg [BW:0] adc_lane_7; + + reg [BW:0] adc_data_init[7:0]; + reg [BW:0] adc_data_store[7:0]; + + reg [ 2:0] lane_0_ch = 3'd0; + reg [ 2:0] lane_1_ch = 3'd0; + reg [ 2:0] lane_2_ch = 3'd0; + reg [ 2:0] lane_3_ch = 3'd0; + reg [ 2:0] lane_4_ch = 3'd0; + reg [ 2:0] lane_5_ch = 3'd0; + reg [ 2:0] lane_6_ch = 3'd0; + reg [ 2:0] lane_7_ch = 3'd0; + + reg [ 3:0] adc_lane0_shift; + reg [ 3:0] adc_lane1_shift; + reg [ 3:0] adc_lane2_shift; + reg [ 3:0] adc_lane3_shift; + reg [ 3:0] adc_lane4_shift; + reg [ 3:0] adc_lane5_shift; + reg [ 3:0] adc_lane6_shift; + reg [ 3:0] adc_lane7_shift; + + reg [ 3:0] adc_lane0_shift_d; + reg [ 3:0] adc_lane1_shift_d; + reg [ 3:0] adc_lane2_shift_d; + reg [ 3:0] adc_lane3_shift_d; + reg [ 3:0] adc_lane4_shift_d; + reg [ 3:0] adc_lane5_shift_d; + reg [ 3:0] adc_lane6_shift_d; + reg [ 3:0] adc_lane7_shift_d; + + reg adc_valid_init; + reg adc_valid_init_d; + + reg [ 7:0] ch_data_lock = 8'hff; + reg [ 7:0] ch_capture; + reg [ 7:0] ch_captured; + + reg scko_d; + reg [7:0] sdo_d; + + reg [ 4:0] sdi_index = 5'd23; + + reg [23:0] softspan_next_int; + + // internal wires + + wire start_transfer_s; + + wire scki_cnt_rst; + + wire acquire_data; + + wire [17:0] adc_data_raw_s [7:0]; + wire [31:0] adc_data_sign_s [7:0]; + wire [31:0] adc_data_zero_s [7:0]; + wire [31:0] adc_data_s [7:0]; + wire [ 2:0] adc_ch_id_s [7:0]; + wire [ 2:0] adc_softspan_s [7:0]; + + always @(posedge clk) begin + if (rst == 1'b1) begin + busy_m1 <= 1'b0; + busy_m2 <= 1'b0; + end else begin + busy_m1 <= busy; + busy_m2 <= busy_m1; + busy_m3 <= busy_m2; + end + end + + assign start_transfer_s = busy_m3 & ~busy_m2; + + // reading clock logic + always @(posedge clk) begin + if (rst) begin + scki_counter <= 5'h0; + scki_i <= 1'b1; + scki_d <= 1'b0; + end else begin + scki_d <= scki_i; + if (acquire_data == 1'b0) begin + scki_counter <= 5'h0; + scki_i <= 1'b1; + end else if (scki_cnt_rst & (scki_d & ~scki_i)) begin // end of a capture + scki_counter <= 5'h1; + scki_i <= 1'b1; + end else if (scki_i == 1'b0) begin + scki_counter <= scki_counter + 5'd1; + scki_i <= 1'b1; + end else begin + scki_counter <= scki_counter; + scki_i <= ~scki_i; + end + end + end + + assign scki_cnt_rst = (scki_counter == DW); + assign scki = scki_i | ~acquire_data; + + /* + The device sends each channel data on one of the 8 lines. + Data is stored in the device in a ring buffer. After the first packet is read + and no new conversion is requested if the reading process is restarted, + the new data on the lines will be from the next index from the ring buffer. + e.g For second read process without a conversion start + line 0 = channel 1, line 1 = channel 2, line 2 = channel 3; so on and so forth. + + The ring buffer contains the crc data on the 8(last position.) + e.g for a 4'th reading cycle: + line 0 = ch 3 + line 1 = ch 4 + line 2 = ch 5 + line 3 = ch 6 + line 4 = ch 7 + line 5 = crc + line 6 = ch 0 + line 7 = ch 1 + + Because there is no rule for a specific number of lanes to be enabled at a given time + the interface can handle every combination of enabled lanes with enabled channels. + The valid signal will only be asserted after all enabled channels are stored. + This means that the user must adjust the sampling frequency based on the + interface clock frequency and the maximum position/index + difference +1 of a channel data and the first enabled lane that will + pass that channels data, maximum difference is 8(e.g line 0 to line 7). + e.g. If only lanes 1 and 2(0 to 7) are enabled, + 1. The user wants to capture the 6'th(0 to 7) channel, 5 reading cycles are required. + 2. The user wants to capture channel 0, 8 reading cycles are required. + */ + + // capture data per lane in rx buffers adc_lane_X on every edge of scko + // ignore when busy forced scko to 0 + always @(posedge clk) begin + scko_d <= scko; + sdo_d <= sdo; + if (scko != scko_d && scki != scki_d) begin + adc_lane_0 <= {adc_lane_0[BW-1:0], sdo_d[0]}; + adc_lane_1 <= {adc_lane_1[BW-1:0], sdo_d[1]}; + adc_lane_2 <= {adc_lane_2[BW-1:0], sdo_d[2]}; + adc_lane_3 <= {adc_lane_3[BW-1:0], sdo_d[3]}; + adc_lane_4 <= {adc_lane_4[BW-1:0], sdo_d[4]}; + adc_lane_5 <= {adc_lane_5[BW-1:0], sdo_d[5]}; + adc_lane_6 <= {adc_lane_6[BW-1:0], sdo_d[6]}; + adc_lane_7 <= {adc_lane_7[BW-1:0], sdo_d[7]}; + end + end + + // store the data from the rx buffers when all bits are received + // when data transaction window is done + // index is based by lane + always @(posedge clk) begin + if (rst == 1'b1) begin + adc_data_init[0] <= 'h0; + adc_data_init[1] <= 'h0; + adc_data_init[2] <= 'h0; + adc_data_init[3] <= 'h0; + adc_data_init[4] <= 'h0; + adc_data_init[5] <= 'h0; + adc_data_init[6] <= 'h0; + adc_data_init[7] <= 'h0; + data_counter <= 5'h0; + end else begin + data_counter <= scki_counter; + if (data_counter == DW) begin + adc_data_init[0] <= adc_lane_0; + adc_data_init[1] <= adc_lane_1; + adc_data_init[2] <= adc_lane_2; + adc_data_init[3] <= adc_lane_3; + adc_data_init[4] <= adc_lane_4; + adc_data_init[5] <= adc_lane_5; + adc_data_init[6] <= adc_lane_6; + adc_data_init[7] <= adc_lane_7; + end + end + end + + /* + lane_X_ch - channel number that lane X has + e.g., lane_0_ch = 2, means lane 0 has channel 2 + ch_data_lock[i] - locks channel i + e.g., ch_data_lock[7] = 1, means data from channel 7 has already been + sent to an active lane, channel 7 should now be locked. + Don't acquire data if all channels are all already locked. + */ + always @(posedge clk) begin + if (start_transfer_s) begin + lane_0_ch <= 3'd0; + lane_1_ch <= 3'd1; + lane_2_ch <= 3'd2; + lane_3_ch <= 3'd3; + lane_4_ch <= 3'd4; + lane_5_ch <= 3'd5; + lane_6_ch <= 3'd6; + lane_7_ch <= 3'd7; + ch_data_lock <= 8'd0; + end else if (acquire_data == 1'b1 && (scki_cnt_rst & (~scki_d & scki_i))) begin + lane_0_ch <= lane_0_ch + 3'd1; + lane_1_ch <= lane_1_ch + 3'd1; + lane_2_ch <= lane_2_ch + 3'd1; + lane_3_ch <= lane_3_ch + 3'd1; + lane_4_ch <= lane_4_ch + 3'd1; + lane_5_ch <= lane_5_ch + 3'd1; + lane_6_ch <= lane_6_ch + 3'd1; + lane_7_ch <= lane_7_ch + 3'd1; + ch_data_lock[lane_0_ch] <= ACTIVE_LANES[0] ? 1'b1 : ch_data_lock[lane_0_ch]; + ch_data_lock[lane_1_ch] <= ACTIVE_LANES[1] ? 1'b1 : ch_data_lock[lane_1_ch]; + ch_data_lock[lane_2_ch] <= ACTIVE_LANES[2] ? 1'b1 : ch_data_lock[lane_2_ch]; + ch_data_lock[lane_3_ch] <= ACTIVE_LANES[3] ? 1'b1 : ch_data_lock[lane_3_ch]; + ch_data_lock[lane_4_ch] <= ACTIVE_LANES[4] ? 1'b1 : ch_data_lock[lane_4_ch]; + ch_data_lock[lane_5_ch] <= ACTIVE_LANES[5] ? 1'b1 : ch_data_lock[lane_5_ch]; + ch_data_lock[lane_6_ch] <= ACTIVE_LANES[6] ? 1'b1 : ch_data_lock[lane_6_ch]; + ch_data_lock[lane_7_ch] <= ACTIVE_LANES[7] ? 1'b1 : ch_data_lock[lane_7_ch]; + end + end + + assign acquire_data = ~((ch_data_lock[0] | ~adc_enable[0]) & + (ch_data_lock[1] | ~adc_enable[1]) & + (ch_data_lock[2] | ~adc_enable[2]) & + (ch_data_lock[3] | ~adc_enable[3]) & + (ch_data_lock[4] | ~adc_enable[4]) & + (ch_data_lock[5] | ~adc_enable[5]) & + (ch_data_lock[6] | ~adc_enable[6]) & + (ch_data_lock[7] | ~adc_enable[7])); + + // hold lane status and lane channel + // for datasyncing with valid signal + always @(posedge clk) begin + if (rst == 1'b1 || adc_valid == 1'b1) begin + adc_lane0_shift <= 4'd0; + adc_lane1_shift <= 4'd0; + adc_lane2_shift <= 4'd0; + adc_lane3_shift <= 4'd0; + adc_lane4_shift <= 4'd0; + adc_lane5_shift <= 4'd0; + adc_lane6_shift <= 4'd0; + adc_lane7_shift <= 4'd0; + adc_lane0_shift_d <= 4'd0; + adc_lane1_shift_d <= 4'd0; + adc_lane2_shift_d <= 4'd0; + adc_lane3_shift_d <= 4'd0; + adc_lane4_shift_d <= 4'd0; + adc_lane5_shift_d <= 4'd0; + adc_lane6_shift_d <= 4'd0; + adc_lane7_shift_d <= 4'd0; + end else begin + adc_lane0_shift <= {ACTIVE_LANES[0], lane_0_ch}; + adc_lane1_shift <= {ACTIVE_LANES[1], lane_1_ch}; + adc_lane2_shift <= {ACTIVE_LANES[2], lane_2_ch}; + adc_lane3_shift <= {ACTIVE_LANES[3], lane_3_ch}; + adc_lane4_shift <= {ACTIVE_LANES[4], lane_4_ch}; + adc_lane5_shift <= {ACTIVE_LANES[5], lane_5_ch}; + adc_lane6_shift <= {ACTIVE_LANES[6], lane_6_ch}; + adc_lane7_shift <= {ACTIVE_LANES[7], lane_7_ch}; + adc_lane0_shift_d <= adc_lane0_shift; + adc_lane1_shift_d <= adc_lane1_shift; + adc_lane2_shift_d <= adc_lane2_shift; + adc_lane3_shift_d <= adc_lane3_shift; + adc_lane4_shift_d <= adc_lane4_shift; + adc_lane5_shift_d <= adc_lane5_shift; + adc_lane6_shift_d <= adc_lane6_shift; + adc_lane7_shift_d <= adc_lane7_shift; + end + end + + // stores the data from the rx buffer, but now based on ch + // index is based on ch, not lane anymore + always @(posedge clk) begin + if (rst == 1'b1) begin + adc_data_store[0] <= 'd0; + adc_data_store[1] <= 'd0; + adc_data_store[2] <= 'd0; + adc_data_store[3] <= 'd0; + adc_data_store[4] <= 'd0; + adc_data_store[5] <= 'd0; + adc_data_store[6] <= 'd0; + adc_data_store[7] <= 'd0; + end else begin + if (!adc_valid_init_d & adc_valid_init) begin + if (adc_lane0_shift_d[3] == 1'b1) begin + adc_data_store[adc_lane0_shift_d[2:0]] <= adc_data_init[0]; + end + if (adc_lane1_shift_d[3] == 1'b1) begin + adc_data_store[adc_lane1_shift_d[2:0]] <= adc_data_init[1]; + end + if (adc_lane2_shift_d[3] == 1'b1) begin + adc_data_store[adc_lane2_shift_d[2:0]] <= adc_data_init[2]; + end + if (adc_lane3_shift_d[3] == 1'b1) begin + adc_data_store[adc_lane3_shift_d[2:0]] <= adc_data_init[3]; + end + if (adc_lane4_shift_d[3] == 1'b1) begin + adc_data_store[adc_lane4_shift_d[2:0]] <= adc_data_init[4]; + end + if (adc_lane5_shift_d[3] == 1'b1) begin + adc_data_store[adc_lane5_shift_d[2:0]] <= adc_data_init[5]; + end + if (adc_lane6_shift_d[3] == 1'b1) begin + adc_data_store[adc_lane6_shift_d[2:0]] <= adc_data_init[6]; + end + if (adc_lane7_shift_d[3] == 1'b1) begin + adc_data_store[adc_lane7_shift_d[2:0]] <= adc_data_init[7]; + end + end + end + end + + // extract info from the data bits + genvar i; + generate + for (i=0; i < 8; i=i+1) begin: format + assign adc_data_raw_s[i] = adc_data_store[i][BW:DW-DATA_WIDTH]; + assign adc_data_sign_s[i] = {{(32-DATA_WIDTH){adc_data_raw_s[i][DATA_WIDTH-1]}}, adc_data_raw_s[i]}; + assign adc_data_zero_s[i] = {{(32-DATA_WIDTH){1'b0}}, adc_data_raw_s[i]}; + assign adc_data_s[i] = (adc_softspan_s[i] == 3'b0)? 32'h0 : + (adc_softspan_s[i][1])? adc_data_sign_s[i] : + adc_data_zero_s[i] ; + if (NUM_CHANNELS == 8) begin + assign adc_ch_id_s[i] = adc_data_store[i][5:3]; + end else if (NUM_CHANNELS == 4) begin + assign adc_ch_id_s[i] = {1'b0, adc_data_store[i][4:3]}; + end else begin + assign adc_ch_id_s[i] = {2'b0, adc_data_store[i][3]}; + end + assign adc_softspan_s[i] = adc_data_store[i][2:0]; + end + endgenerate + + // assign extracted adc data to corresponding outputs + assign adc_data_0 = adc_data_s[0]; + assign adc_data_1 = adc_data_s[1]; + assign adc_data_2 = adc_data_s[2]; + assign adc_data_3 = adc_data_s[3]; + assign adc_data_4 = adc_data_s[4]; + assign adc_data_5 = adc_data_s[5]; + assign adc_data_6 = adc_data_s[6]; + assign adc_data_7 = adc_data_s[7]; + + // assign extracted adc channel id to corresponding outputs + assign adc_ch0_id = adc_ch_id_s[0]; + assign adc_ch1_id = adc_ch_id_s[1]; + assign adc_ch2_id = adc_ch_id_s[2]; + assign adc_ch3_id = adc_ch_id_s[3]; + assign adc_ch4_id = adc_ch_id_s[4]; + assign adc_ch5_id = adc_ch_id_s[5]; + assign adc_ch6_id = adc_ch_id_s[6]; + assign adc_ch7_id = adc_ch_id_s[7]; + + // assign extracted adc channel id to corresponding outputs + assign adc_softspan_0 = adc_softspan_s[0]; + assign adc_softspan_1 = adc_softspan_s[1]; + assign adc_softspan_2 = adc_softspan_s[2]; + assign adc_softspan_3 = adc_softspan_s[3]; + assign adc_softspan_4 = adc_softspan_s[4]; + assign adc_softspan_5 = adc_softspan_s[5]; + assign adc_softspan_6 = adc_softspan_s[6]; + assign adc_softspan_7 = adc_softspan_s[7]; + + // initial valid signal + always @(posedge clk) begin + if (rst == 1'b1) begin + adc_valid_init <= 1'b0; + end else begin + if (data_counter == DW && adc_valid_init == 1'b0) begin + adc_valid_init <= 1'b1; + end else begin + adc_valid_init <= 1'b0; + end + end + end + + // delayed both valid signal and data_lock signal + // for datasyncing with valid signal + always @(posedge clk) begin + if (rst == 1'b1 || adc_valid == 1'b1) begin + adc_valid <= 1'b0; + adc_valid_init_d <= 1'b0; + ch_capture <= 8'd0; + ch_captured <= 8'd0; + end else begin + ch_capture <= ch_data_lock; + ch_captured <= ch_capture; + adc_valid_init_d <= adc_valid_init; + adc_valid <= adc_valid_init_d & + (ch_captured[0] | ~adc_enable[0]) & + (ch_captured[1] | ~adc_enable[1]) & + (ch_captured[2] | ~adc_enable[2]) & + (ch_captured[3] | ~adc_enable[3]) & + (ch_captured[4] | ~adc_enable[4]) & + (ch_captured[5] | ~adc_enable[5]) & + (ch_captured[6] | ~adc_enable[6]) & + (ch_captured[7] | ~adc_enable[7]); + end + end + + // every negedge of scki, update index of sdi + always @(posedge clk) begin + if (start_transfer_s || rst) begin + sdi_index <= 5'd23; + end else begin + if (scki && !scki_d && sdi_index != 5'b11111) begin + sdi_index <= sdi_index - 5'b1; + end + end + end + + // update next softspan configuration every after busy + always @(posedge clk) begin + if (rst == 1'b1) begin + softspan_next_int <= 24'hff_ffff; + end else begin + if (busy_m3 & ~busy_m2) begin + softspan_next_int <= softspan_next; + end else begin + softspan_next_int <= softspan_next_int; + end + end + end + + assign sdi = (sdi_index != 5'b11111)? softspan_next_int[sdi_index] : 1'b0; + +endmodule diff --git a/library/axi_ltc235x/axi_ltc235x_hw.tcl b/library/axi_ltc235x/axi_ltc235x_hw.tcl new file mode 100644 index 00000000000..37ff5985679 --- /dev/null +++ b/library/axi_ltc235x/axi_ltc235x_hw.tcl @@ -0,0 +1,93 @@ +# ip + +package require qsys 14.0 +package require quartus::device + +source ../../scripts/adi_env.tcl +source ../scripts/adi_ip_intel.tcl + +ad_ip_create axi_ltc235x {AXI LTC235x Interface} axi_ltc235x_elab +set_module_property AUTHOR {Geronimo, John Erasmus Mari F.} +set_module_property VALIDATION_CALLBACK info_param_validate +ad_ip_files axi_ltc235x [list \ + $ad_hdl_dir/library/common/up_axi.v \ + $ad_hdl_dir/library/common/up_adc_common.v \ + $ad_hdl_dir/library/common/ad_rst.v \ + $ad_hdl_dir/library/common/up_xfer_cntrl.v \ + $ad_hdl_dir/library/common/up_xfer_status.v \ + $ad_hdl_dir/library/common/up_clock_mon.v \ + $ad_hdl_dir/library/common/up_adc_channel.v \ + $ad_hdl_dir/library/intel/common/up_xfer_cntrl_constr.sdc \ + $ad_hdl_dir/library/intel/common/up_rst_constr.sdc \ + $ad_hdl_dir/library/intel/common/up_xfer_status_constr.sdc \ + $ad_hdl_dir/library/intel/common/up_clock_mon_constr.sdc \ + axi_ltc235x_cmos.v \ + axi_ltc235x_lvds.v \ + axi_ltc235x.v] +add_documentation_link "AXI_LTC235x IP core" https://wiki.analog.com/resources/fpga/docs/axi_ltc235x + +ad_ip_parameter ID INTEGER 0 +ad_ip_parameter XILINX_INTEL_N INTEGER 0 +ad_ip_parameter LVDS_CMOS_N INTEGER 0 +ad_ip_parameter LANE_0_ENABLE INTEGER 1 +ad_ip_parameter LANE_1_ENABLE INTEGER 1 +ad_ip_parameter LANE_2_ENABLE INTEGER 1 +ad_ip_parameter LANE_3_ENABLE INTEGER 1 +ad_ip_parameter LANE_4_ENABLE INTEGER 1 +ad_ip_parameter LANE_5_ENABLE INTEGER 1 +ad_ip_parameter LANE_6_ENABLE INTEGER 1 +ad_ip_parameter LANE_7_ENABLE INTEGER 1 +ad_ip_parameter EXTERNAL_CLK INTEGER 0 +ad_ip_parameter LTC235X_FAMILY INTEGER 0 +ad_ip_parameter NUM_CHANNELS INTEGER 8 +ad_ip_parameter DATA_WIDTH INTEGER 18 + +adi_add_auto_fpga_spec_params + +proc axi_ltc235x_elab {} { + # interfaces + + # physical interface + add_interface device_if conduit end + # common + add_interface_port device_if busy busy Input 1 + add_interface_port device_if lvds_cmos_n lvds_cmos_n Output 1 + + set interface [get_parameter_value LVDS_CMOS_N] + switch $interface { + "0" { ;# cmos + add_interface_port device_if scki scki Output 1 + add_interface_port device_if scko scko Input 1 + add_interface_port device_if sdi sdi Output 1 + add_interface_port device_if sdo sdo Input 8} + "1" { ;# lvds + add_interface_port device_if scki_p scki_p Output 1 + add_interface_port device_if scki_n scki_n Output 1 + add_interface_port device_if scko_p scko_p Input 1 + add_interface_port device_if scko_n scko_n Input 1 + add_interface_port device_if sdi_p sdi_p Output 1 + add_interface_port device_if sdi_n sdi_n Output 1 + add_interface_port device_if sdo_p sdo_p Input 1 + add_interface_port device_if sdo_n sdo_n Input 1} + } + + # clock + ad_interface clock external_clk input 1 + + # axi + ad_ip_intf_s_axi s_axi_aclk s_axi_aresetn + + # others + ad_interface signal adc_dovf Input 1 ovf + + set num_channels [get_parameter_value NUM_CHANNELS] + for {set i 0} {$i < $num_channels} {incr i} { + add_interface adc_ch_$i conduit end + add_interface_port adc_ch_$i adc_enable_$i enable Output 1 + add_interface_port adc_ch_$i adc_valid_$i valid Output 1 + add_interface_port adc_ch_$i adc_data_$i data Output 32 + + set_interface_property adc_ch_$i associatedClock if_external_clk + set_interface_property adc_ch_$i associatedReset "" + } +} diff --git a/library/axi_ltc235x/axi_ltc235x_lvds.v b/library/axi_ltc235x/axi_ltc235x_lvds.v new file mode 100644 index 00000000000..1fe37041f50 --- /dev/null +++ b/library/axi_ltc235x/axi_ltc235x_lvds.v @@ -0,0 +1,427 @@ +// *************************************************************************** +// *************************************************************************** +// Copyright 2023 (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. +// +// *************************************************************************** +// *************************************************************************** + +`timescale 1ns/100ps + +module axi_ltc235x_lvds #( + parameter XILINX_INTEL_N = 0, + parameter LTC235X_FAMILY = 0, + parameter NUM_CHANNELS = 8, // 8 for 2358, 4 for 2357, 2 for 2353 + parameter DATA_WIDTH = 18 // 18 or 16 based on -18/-16 +) ( + + input rst, + input clk, + input [ 7:0] adc_enable, + input [23:0] softspan_next, + + // physical interface + + output scki_p, + output scki_n, + output sdi_p, + output sdi_n, + input scko_p, + input scko_n, + input sdo_p, + input sdo_n, + input busy, + + // FIFO interface + + output [ 2:0] adc_ch0_id, + output [ 2:0] adc_ch1_id, + output [ 2:0] adc_ch2_id, + output [ 2:0] adc_ch3_id, + output [ 2:0] adc_ch4_id, + output [ 2:0] adc_ch5_id, + output [ 2:0] adc_ch6_id, + output [ 2:0] adc_ch7_id, + + output [31:0] adc_data_0, + output [31:0] adc_data_1, + output [31:0] adc_data_2, + output [31:0] adc_data_3, + output [31:0] adc_data_4, + output [31:0] adc_data_5, + output [31:0] adc_data_6, + output [31:0] adc_data_7, + + output [ 2:0] adc_softspan_0, + output [ 2:0] adc_softspan_1, + output [ 2:0] adc_softspan_2, + output [ 2:0] adc_softspan_3, + output [ 2:0] adc_softspan_4, + output [ 2:0] adc_softspan_5, + output [ 2:0] adc_softspan_6, + output [ 2:0] adc_softspan_7, + + output reg adc_valid +); + + // local parameters + + localparam DW = 24; // packet size per channel + localparam DW_8 = 24 * 8; // packet size for all 8 channels + localparam BW = DW - 1; + localparam BW_8 = DW_8 - 1; + + // internal registers + + reg busy_m1; + reg busy_m2; + reg busy_m3; + + reg [ 8:0] scki_counter = 9'h0; + reg [ 8:0] data_counter = 9'h0; + + reg scki_i; + reg scki_d; + + reg [BW_8:0] adc_lane; + reg [BW_8:0] adc_data_init; + reg [BW:0] adc_data_store[7:0]; + + reg [ 3:0] adc_lane0_shift; + reg [ 3:0] adc_lane1_shift; + reg [ 3:0] adc_lane2_shift; + reg [ 3:0] adc_lane3_shift; + reg [ 3:0] adc_lane4_shift; + reg [ 3:0] adc_lane5_shift; + reg [ 3:0] adc_lane6_shift; + reg [ 3:0] adc_lane7_shift; + + reg [ 3:0] adc_lane0_shift_d; + reg [ 3:0] adc_lane1_shift_d; + reg [ 3:0] adc_lane2_shift_d; + reg [ 3:0] adc_lane3_shift_d; + reg [ 3:0] adc_lane4_shift_d; + reg [ 3:0] adc_lane5_shift_d; + reg [ 3:0] adc_lane6_shift_d; + reg [ 3:0] adc_lane7_shift_d; + + reg adc_valid_init; + reg adc_valid_init_d; + + reg ch_data_lock = 1; + reg ch_capture; + reg ch_captured; + + reg scko_d; + reg sdo_d; + + reg [ 4:0] sdi_index = 5'd23; + + reg [23:0] softspan_next_int; + + // internal wires + + wire start_transfer_s; + + wire scki_cnt_rst; + + wire acquire_data; + + wire [DATA_WIDTH-1:0] adc_data_raw_s [7:0]; + wire [31:0] adc_data_sign_s [7:0]; + wire [31:0] adc_data_zero_s [7:0]; + wire [31:0] adc_data_s [7:0]; + wire [ 2:0] adc_ch_id_s [7:0]; + wire [ 2:0] adc_softspan_s [7:0]; + + wire scki; + wire sdi; + wire scko; + wire sdo; + + generate + if (XILINX_INTEL_N == 0) begin + assign scki_p = scki; + assign scki_n = 1'b0; + assign sdi_p = sdi; + assign sdi_n = 1'b0; + assign scko = scko_p; + assign sdo = sdo_p; + end else begin + OBUFDS obufds_scki ( + .O(scki_n), + .OB(scki_p), + .I(scki)); + + OBUFDS obufds_sdi ( + .O(sdi_n), + .OB(sdi_p), + .I(sdi)); + + IBUFDS #( + .CCIO_EN_M("TRUE"), + .CCIO_EN_S("TRUE") + ) ibufds_scko ( + .O(scko), + .I(scko_p), + .IB(scko_n)); + + IBUFDS #( + .CCIO_EN_M("TRUE"), + .CCIO_EN_S("TRUE") + ) ibufds_sdo ( + .O(sdo), + .I(sdo_p), + .IB(sdo_n)); + end + endgenerate + + always @(posedge clk) begin + if (rst == 1'b1) begin + busy_m1 <= 1'b0; + busy_m2 <= 1'b0; + end else begin + busy_m1 <= busy; + busy_m2 <= busy_m1; + busy_m3 <= busy_m2; + end + end + + assign start_transfer_s = busy_m3 & ~busy_m2; + + // reading clock logic + always @(posedge clk) begin + if (rst) begin + scki_counter <= 9'h0; + scki_i <= 1'b0; + scki_d <= 1'b0; + end else begin + scki_d <= scki_i; + if (acquire_data == 1'b0) begin + scki_counter <= 9'h0; + scki_i <= 1'b0; + end else if (scki_cnt_rst & (scki_d != scki_i)) begin // end of a capture + scki_counter <= 9'h1; + scki_i <= 1'b0; + end else begin + scki_counter <= scki_counter + 9'd1; + scki_i <= ~scki_i; + end + end + end + + assign scki_cnt_rst = (scki_counter == DW_8); + assign scki = scki_i & acquire_data; + + // capture data per lane in rx buffer on every edge of scko + // ignore when busy forced scko to 0 + always @(posedge clk) begin + scko_d <= scko; + sdo_d <= sdo; + if (scko != scko_d && scki != scki_d) begin + adc_lane <= {adc_lane[BW_8-1:0], sdo_d}; + end + end + + // store the data from the rx buffers when all bits are received + // when data transaction window is done + // index is based by lane + always @(posedge clk) begin + if (rst == 1'b1) begin + adc_data_init <= 'h0; + data_counter <= 9'h0; + end else begin + data_counter <= scki_counter; + if (data_counter == DW_8) begin + adc_data_init <= adc_lane; + end + end + end + + // lane_x_data - ch corresponds to which lane + // ch_data_lock[i] - locks ch i, means dont acquire data if all ch's are lock while acquire_data = 0 + always @(posedge clk) begin + if (start_transfer_s) begin + ch_data_lock <= 1'd0; + end else if (acquire_data == 1'b1 && (scki_cnt_rst & (scki_d != scki_i))) begin + ch_data_lock <= 1'd1; + end + end + + assign acquire_data = ~((ch_data_lock | ~adc_enable[0]) & + (ch_data_lock | ~adc_enable[1]) & + (ch_data_lock | ~adc_enable[2]) & + (ch_data_lock | ~adc_enable[3]) & + (ch_data_lock | ~adc_enable[4]) & + (ch_data_lock | ~adc_enable[5]) & + (ch_data_lock | ~adc_enable[6]) & + (ch_data_lock | ~adc_enable[7])); + + // stores the data from the rx buffer, but now based on ch + // from the whole buffer into the per channel buffer + always @(posedge clk) begin + if (rst == 1'b1) begin + adc_data_store[0] <= 'd0; + adc_data_store[1] <= 'd0; + adc_data_store[2] <= 'd0; + adc_data_store[3] <= 'd0; + adc_data_store[4] <= 'd0; + adc_data_store[5] <= 'd0; + adc_data_store[6] <= 'd0; + adc_data_store[7] <= 'd0; + end else begin + if (!adc_valid_init_d & adc_valid_init) begin + adc_data_store[0] <= adc_data_init[BW + DW * 7: DW * 7]; + adc_data_store[1] <= adc_data_init[BW + DW * 6: DW * 6]; + adc_data_store[2] <= adc_data_init[BW + DW * 5: DW * 5]; + adc_data_store[3] <= adc_data_init[BW + DW * 4: DW * 4]; + adc_data_store[4] <= adc_data_init[BW + DW * 3: DW * 3]; + adc_data_store[5] <= adc_data_init[BW + DW * 2: DW * 2]; + adc_data_store[6] <= adc_data_init[BW + DW * 1: DW * 1]; + adc_data_store[7] <= adc_data_init[BW + DW * 0: DW * 0]; + end + end + end + + // extract info from the data bits + genvar i; + generate + for (i=0; i < 8; i=i+1) begin: format + assign adc_data_raw_s[i] = adc_data_store[i][BW:DW-DATA_WIDTH]; + assign adc_data_sign_s[i] = {{(32-DATA_WIDTH){adc_data_raw_s[i][DATA_WIDTH-1]}}, adc_data_raw_s[i]}; + assign adc_data_zero_s[i] = {{(32-DATA_WIDTH){1'b0}}, adc_data_raw_s[i]}; + assign adc_data_s[i] = (adc_softspan_s[i] == 3'b0)? 32'h0 : + (adc_softspan_s[i][1])? adc_data_sign_s[i] : + adc_data_zero_s[i] ; + if (NUM_CHANNELS == 8) begin + assign adc_ch_id_s[i] = adc_data_store[i][5:3]; + end else if (NUM_CHANNELS == 4) begin + assign adc_ch_id_s[i] = {1'b0, adc_data_store[i][4:3]}; + end else begin + assign adc_ch_id_s[i] = {2'b0, adc_data_store[i][3]}; + end + assign adc_softspan_s[i] = adc_data_store[i][2:0]; + end + endgenerate + + // assign extracted adc data to corresponding outputs + assign adc_data_0 = adc_data_s[0]; + assign adc_data_1 = adc_data_s[1]; + assign adc_data_2 = adc_data_s[2]; + assign adc_data_3 = adc_data_s[3]; + assign adc_data_4 = adc_data_s[4]; + assign adc_data_5 = adc_data_s[5]; + assign adc_data_6 = adc_data_s[6]; + assign adc_data_7 = adc_data_s[7]; + + // assign extracted adc channel id to corresponding outputs + assign adc_ch0_id = adc_ch_id_s[0]; + assign adc_ch1_id = adc_ch_id_s[1]; + assign adc_ch2_id = adc_ch_id_s[2]; + assign adc_ch3_id = adc_ch_id_s[3]; + assign adc_ch4_id = adc_ch_id_s[4]; + assign adc_ch5_id = adc_ch_id_s[5]; + assign adc_ch6_id = adc_ch_id_s[6]; + assign adc_ch7_id = adc_ch_id_s[7]; + + // assign extracted adc channel id to corresponding outputs + assign adc_softspan_0 = adc_softspan_s[0]; + assign adc_softspan_1 = adc_softspan_s[1]; + assign adc_softspan_2 = adc_softspan_s[2]; + assign adc_softspan_3 = adc_softspan_s[3]; + assign adc_softspan_4 = adc_softspan_s[4]; + assign adc_softspan_5 = adc_softspan_s[5]; + assign adc_softspan_6 = adc_softspan_s[6]; + assign adc_softspan_7 = adc_softspan_s[7]; + + // initial valid signal + always @(posedge clk) begin + if (rst == 1'b1) begin + adc_valid_init <= 1'b0; + end else begin + if (data_counter == DW_8 && adc_valid_init == 1'b0) begin + adc_valid_init <= 1'b1; + end else begin + adc_valid_init <= 1'b0; + end + end + end + + // delayed both valid signal and data_lock signal + // for datasyncing with valid signal + always @(posedge clk) begin + if (rst == 1'b1 || adc_valid == 1'b1) begin + adc_valid <= 1'b0; + adc_valid_init_d <= 1'b0; + ch_capture <= 1'd0; + ch_captured <= 1'd0; + end else begin + ch_capture <= ch_data_lock; + ch_captured <= ch_capture; + adc_valid_init_d <= adc_valid_init; + adc_valid <= adc_valid_init_d & + (ch_captured | ~adc_enable[0]) & + (ch_captured | ~adc_enable[1]) & + (ch_captured | ~adc_enable[2]) & + (ch_captured | ~adc_enable[3]) & + (ch_captured | ~adc_enable[4]) & + (ch_captured | ~adc_enable[5]) & + (ch_captured | ~adc_enable[6]) & + (ch_captured | ~adc_enable[7]); + end + end + + // every negedge of clk, update index of sdi + always @(negedge clk) begin + if (start_transfer_s || rst) begin + sdi_index <= 5'd23; + end else begin + if (scki != scki_d && sdi_index != 5'b11111) begin + sdi_index <= sdi_index - 5'b1; + end + end + end + + // update next softspan configuration every after busy + always @(posedge clk) begin + if (rst == 1'b1) begin + softspan_next_int <= 24'hff_ffff; + end else begin + if (busy_m3 & ~busy_m2) begin + softspan_next_int <= softspan_next; + end else begin + softspan_next_int <= softspan_next_int; + end + end + end + + assign sdi = (sdi_index != 5'b11111)? softspan_next_int[sdi_index] : 1'b0; + +endmodule