// *************************************************************************** // *************************************************************************** // Copyright (C) 2023 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/main/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_ad4858_lvds #( parameter FPGA_TECHNOLOGY = 0, parameter DELAY_REFCLK_FREQ = 200, parameter IODELAY_ENABLE = 1, parameter NEG_EDGE = 1 ) ( input rst, input clk, input fast_clk, input [ 7:0] adc_enable, input adc_crc_enable, // physical interface output scki_p, output scki_n, input scko_p, input scko_n, input sdo_p, input sdo_n, input busy, input cnvs, // format input [ 1:0] packet_format_in, input oversampling_en, // channel interface output reg [255:0] adc_data, output reg adc_valid, output reg crc_error, output reg [ 7:0] dev_status, // delay interface (for IDELAY macros) input up_clk, input up_adc_dld, input [ 4:0] up_adc_dwdata, output [ 4:0] up_adc_drdata, input delay_clk, input delay_rst, output delay_locked ); localparam DW = 32; localparam BW = DW - 1; // internal registers reg [ 1:0] packet_format; reg [ 5:0] packet_cnt_length; reg [ 15:0] crc_data_length; reg [143:0] rx_data_pos; reg [143:0] rx_data_neg; reg [ 5:0] data_counter = 'h0; reg [ 3:0] ch_counter = 'h0; reg busy_m1; reg busy_m2; reg cnvs_d; reg [ 31:0] period_cnt; reg conversion_quiet_time; reg run_busy_period_cnt; reg [ 31:0] busy_conversion_cnt; reg [ 31:0] busy_measure_value; reg [ 31:0] busy_measure_value_plus; reg [287:0] adc_data_store; reg [287:0] adc_data_init; reg aquire_data; reg capture_complete_init; reg capture_complete_d; reg start_transfer; reg start_transfer_d; reg [ 15:0] dynamic_delay; reg adc_valid_init; reg adc_valid_init_d; reg adc_valid_init_d2; reg [255:0] adc_data_0; reg [255:0] adc_data_1; reg [255:0] adc_data_2; reg crc_enable_window; reg run_crc; reg run_crc_d; reg [287:0] crc_data_in; reg [287:0] crc_data_in_sh; reg [ 15:0] crc_cnt; reg [ 7:0] data_in_byte; reg [ 3:0] ch_7_index; reg [ 3:0] ch_6_index; reg [ 3:0] ch_5_index; reg [ 3:0] ch_4_index; reg [ 3:0] ch_3_index; reg [ 3:0] ch_2_index; reg [ 3:0] ch_1_index; reg [ 3:0] ch_0_index; reg [ 8:0] ch_7_base; reg [ 8:0] ch_6_base; reg [ 8:0] ch_5_base; reg [ 8:0] ch_4_base; reg [ 8:0] ch_3_base; reg [ 8:0] ch_2_base; reg [ 8:0] ch_1_base; reg [ 8:0] ch_0_base; reg [ 3:0] max_channel_transfer; reg [ 31:0] device_status_store; // internal wires wire scko; wire scko_s; wire sdo; wire conversion_completed; wire conversion_quiet_time_s; wire capture_complete_s; wire capture_complete; wire crc_reset; wire crc_enable; wire crc_valid; wire [ 15:0] crc_res; wire sdo_p_s; wire sdo_n_s; wire [ 8:0] ch_index_20_pack [8:0]; wire [ 8:0] ch_index_24_pack [8:0]; wire [ 8:0] ch_index_32_pack [8:0]; wire [143:0] rx_data_pos_s; wire [143:0] rx_data_neg_s; // assgments genvar j; generate for (j = 0; j <= 8; j = j + 1) begin assign ch_index_20_pack [j] = j * 20; assign ch_index_24_pack [j] = j * 24; assign ch_index_32_pack [j] = j * 32; end endgenerate always @(posedge clk) begin busy_m1 <= busy; busy_m2 <= busy_m1; start_transfer <= busy_m2 & !busy_m1; start_transfer_d <= start_transfer; end always @(posedge clk) begin packet_format <= packet_format_in; if (start_transfer) begin packet_cnt_length <= packet_format == 2'd0 ? 6'd20 - 6'd4 : packet_format == 2'd1 ? 6'd24 - 6'd4 : 6'd32 - 6'd4; max_channel_transfer <= adc_crc_enable ? 8 : 7; crc_enable_window <= adc_crc_enable; end end // busy period counter always @(posedge clk) begin if (cnvs == 1'b1 && busy_m2 == 1'b1) begin run_busy_period_cnt <= 1'b1; end else if (start_transfer == 1'b1) begin run_busy_period_cnt <= 1'b0; end end always @(posedge clk) begin if (adc_cnvs_redge == 1'b1) begin busy_conversion_cnt <= - 'd1; //adj for + 1 clk cycle measurement error end else if (run_busy_period_cnt == 1'b1) begin busy_conversion_cnt <= busy_conversion_cnt + 'd1; end end always @(posedge clk) begin if (start_transfer == 1'b1) begin busy_measure_value <= busy_conversion_cnt; end end always @(posedge clk) begin cnvs_d <= cnvs; if (oversampling_en == 1 && adc_cnvs_redge == 1'b1) begin conversion_quiet_time <= 1'b1; end else begin conversion_quiet_time <= conversion_quiet_time & ~conversion_completed; end if (adc_cnvs_redge == 1'b1) begin period_cnt <= 'd0; end else begin period_cnt <= period_cnt + 1; end end assign conversion_quiet_time_s = (oversampling_en == 1) ? conversion_quiet_time & !conversion_completed | cnvs : 1'b0; assign conversion_completed = (period_cnt == busy_measure_value) ? 1'b1 : 1'b0; assign adc_cnvs_redge = ~cnvs_d & cnvs; always @(posedge clk) begin if (aquire_data == 1'b0 || data_counter == packet_cnt_length) begin data_counter <= 2'h0; end else begin data_counter <= data_counter + 4; end if (start_transfer == 1'b1) begin ch_counter <= 4'h0; end else begin if (data_counter == packet_cnt_length) begin if (ch_counter == max_channel_transfer) begin ch_counter <= 4'h0; end else begin ch_counter <= ch_counter + 1; end end else begin ch_counter <= ch_counter; end end if (data_counter == packet_cnt_length && ch_counter == max_channel_transfer) begin aquire_data <= 1'b0; capture_complete_init <= 1'b1; end else if (aquire_data | start_transfer) begin aquire_data <= ~conversion_quiet_time_s; capture_complete_init <= 1'b0; end capture_complete_d <= capture_complete_init; end assign capture_complete_s = ~capture_complete_d & capture_complete_init; // valid delay (0 to 15) always @(posedge clk) begin dynamic_delay <= {dynamic_delay[14:0], capture_complete_s}; end assign capture_complete = dynamic_delay[4'd10]; IBUFDS i_scko_bufds ( .O(scko_s), .I(scko_p), .IB(scko_n)); // It is added to constraint the tool to keep the logic in the same region // as the input pins, otherwise the tool will automatically add a bufg and // meeting the timing margins is harder. BUFH BUFH_inst ( .O(scko), .I(scko_s) ); // receive ad_data_in #( .FPGA_TECHNOLOGY (FPGA_TECHNOLOGY), .REFCLK_FREQUENCY (DELAY_REFCLK_FREQ), .IODELAY_CTRL (1), .IODELAY_ENABLE (IODELAY_ENABLE), .IDDR_CLK_EDGE ("OPPOSITE_EDGE") ) i_rx ( .rx_clk (scko), .rx_data_in_p (sdo_p), .rx_data_in_n (sdo_n), .rx_data_p (sdo_p_s), .rx_data_n (sdo_n_s), .up_clk (up_clk), .up_dld (up_adc_dld), .up_dwdata (up_adc_dwdata[4:0]), .up_drdata (up_adc_drdata[4:0]), .delay_clk (delay_clk), .delay_rst (delay_rst), .delay_locked (delay_locked)); always @(posedge scko) begin rx_data_pos <= {rx_data_pos[142:0], sdo_p_s}; end always @(negedge scko) begin rx_data_neg <= {rx_data_neg[142:0], sdo_n_s}; end assign rx_data_pos_s = {rx_data_pos[142:0], sdo_p_s}; assign rx_data_neg_s = {rx_data_neg[142:0], sdo_n_s}; genvar i; generate for (i = 0; i <= 288 - 2; i = i + 2) begin always @(posedge clk) begin if (capture_complete) begin adc_data_init[i+:2] <= {rx_data_pos_s[i>>1], rx_data_neg_s[i>>1]}; end end end endgenerate always @(posedge clk) begin adc_valid_init <= capture_complete; adc_valid_init_d <= adc_valid_init; adc_valid_init_d2 <= adc_valid_init_d; adc_valid <= adc_valid_init_d2; adc_data_store <= adc_data_init; // relax timing end always @(posedge clk) begin if (start_transfer_d) begin ch_7_index <= crc_enable_window ? 1 : 0; ch_6_index <= crc_enable_window ? 2 : 1; ch_5_index <= crc_enable_window ? 3 : 2; ch_4_index <= crc_enable_window ? 4 : 3; ch_3_index <= crc_enable_window ? 5 : 4; ch_2_index <= crc_enable_window ? 6 : 5; ch_1_index <= crc_enable_window ? 7 : 6; ch_0_index <= crc_enable_window ? 8 : 7; end ch_7_base <= packet_format == 0 ? ch_index_20_pack[ch_7_index] : packet_format == 1 ? ch_index_24_pack[ch_7_index] : ch_index_32_pack[ch_7_index]; ch_6_base <= packet_format == 0 ? ch_index_20_pack[ch_6_index] : packet_format == 1 ? ch_index_24_pack[ch_6_index] : ch_index_32_pack[ch_6_index]; ch_5_base <= packet_format == 0 ? ch_index_20_pack[ch_5_index] : packet_format == 1 ? ch_index_24_pack[ch_5_index] : ch_index_32_pack[ch_5_index]; ch_4_base <= packet_format == 0 ? ch_index_20_pack[ch_4_index] : packet_format == 1 ? ch_index_24_pack[ch_4_index] : ch_index_32_pack[ch_4_index]; ch_3_base <= packet_format == 0 ? ch_index_20_pack[ch_3_index] : packet_format == 1 ? ch_index_24_pack[ch_3_index] : ch_index_32_pack[ch_3_index]; ch_2_base <= packet_format == 0 ? ch_index_20_pack[ch_2_index] : packet_format == 1 ? ch_index_24_pack[ch_2_index] : ch_index_32_pack[ch_2_index]; ch_1_base <= packet_format == 0 ? ch_index_20_pack[ch_1_index] : packet_format == 1 ? ch_index_24_pack[ch_1_index] : ch_index_32_pack[ch_1_index]; ch_0_base <= packet_format == 0 ? ch_index_20_pack[ch_0_index] : packet_format == 1 ? ch_index_24_pack[ch_0_index] : ch_index_32_pack[ch_0_index]; end always @(posedge clk) begin adc_data_0 <={12'b0,adc_data_store[ch_7_base+:20], 12'b0,adc_data_store[ch_6_base+:20], 12'b0,adc_data_store[ch_5_base+:20], 12'b0,adc_data_store[ch_4_base+:20], 12'b0,adc_data_store[ch_3_base+:20], 12'b0,adc_data_store[ch_2_base+:20], 12'b0,adc_data_store[ch_1_base+:20], 12'b0,adc_data_store[ch_0_base+:20]}; adc_data_1 <={8'b0,adc_data_store[ch_7_base+:24], 8'b0,adc_data_store[ch_6_base+:24], 8'b0,adc_data_store[ch_5_base+:24], 8'b0,adc_data_store[ch_4_base+:24], 8'b0,adc_data_store[ch_3_base+:24], 8'b0,adc_data_store[ch_2_base+:24], 8'b0,adc_data_store[ch_1_base+:24], 8'b0,adc_data_store[ch_0_base+:24]}; adc_data_2 <={adc_data_store[ch_7_base+:32], adc_data_store[ch_6_base+:32], adc_data_store[ch_5_base+:32], adc_data_store[ch_4_base+:32], adc_data_store[ch_3_base+:32], adc_data_store[ch_2_base+:32], adc_data_store[ch_1_base+:32], adc_data_store[ch_0_base+:32]}; end always @(posedge clk) begin if (crc_enable_window == 1'b1) begin device_status_store <= adc_data_store[31:0]; end else begin device_status_store <= 0; end end always @(posedge clk) begin case (packet_format) 2'h0: begin adc_data <= adc_data_0; dev_status <= device_status_store[19:0]; end 2'h1: begin adc_data <= adc_data_1; dev_status <= device_status_store[23:0]; end 2'h2: begin adc_data <= adc_data_2; dev_status <= device_status_store[31:0]; end 2'h3: begin adc_data <= adc_data_2; dev_status <= device_status_store[31:0]; end endcase end // CRC checker logic always @(posedge clk) begin if (adc_valid_init_d == 1) begin if (packet_format == 0) begin crc_data_in <= {adc_data_store[179:0], 108'd0}; end else if (packet_format == 1) begin crc_data_in <= {adc_data_store[215:0], 72'd0}; end else begin crc_data_in <= {adc_data_store}; end end end // As an optimization, a crc checker with 8 parallel operations per clk cycle // will be used for all packet formats. // The channel plus status and crc data will be feed in byte packets to the // crc checker. // When packet_format is 20 bits, 20x8+20(st and crc) = 180 which is not a // multiple of 8. So, we will feed 184 bits, which is a multiple of 8. // Last extra 4 bits entering the checker being 0, will not affect the result always @(posedge clk) begin crc_data_length <= packet_format == 2'd0 ? 16'd176 : // 184-8 packet_format == 2'd1 ? 16'd208 : 16'd280; end always @(posedge clk) begin if (adc_valid_init_d2 == 1'b1) begin crc_cnt <= crc_data_length; run_crc <= (crc_enable_window == 1) ? 1'b1 : 1'b0; end else begin if (run_crc == 1'b1) begin if (crc_cnt == 0) begin run_crc <= 1'b0; end else begin crc_cnt <= crc_cnt - 8; end end end // the counter is initialized with n-1 to accommodate the byte shifter run_crc_d <= run_crc; end always @(posedge clk) begin if (adc_valid_init_d2 == 1'b1) begin crc_data_in_sh <= crc_data_in; end else if (run_crc == 1'b1) begin crc_data_in_sh <= crc_data_in_sh << 8; end end always @(posedge clk) begin if (run_crc == 1'b1) begin data_in_byte <= crc_data_in_sh[287:280]; end else begin data_in_byte <= 8'd0; end end assign crc_reset = adc_valid_init_d2; assign crc_enable = run_crc_d | run_crc; always @(posedge clk) begin if (crc_valid == 1) begin if (crc_res == 16'd0) begin crc_error <= 1'd0; end else begin crc_error <= 1'd1; end end end axi_ad4858_crc i_ad4858_crc_8 ( .rst (crc_reset), .clk (clk), .crc_en (crc_enable), .d_in (data_in_byte), .crc_valid (crc_valid), .crc_res (crc_res)); ad_serdes_out #( .FPGA_TECHNOLOGY(FPGA_TECHNOLOGY), .DDR_OR_SDR_N(1'b1), .DATA_WIDTH(1), .SERDES_FACTOR(4) ) i_scki_out ( .rst(rst), .clk(fast_clk), .div_clk(clk), .data_oe(1'b1), .data_s0(1'b0), .data_s1(aquire_data), .data_s2(1'b0), .data_s3(aquire_data), .data_s4(), .data_s5(), .data_s6(), .data_s7(), .data_out_se (), .data_out_p(scki_p), .data_out_n(scki_n)); endmodule