pluto_hdl_adi/library/axi_ltc235x/axi_ltc235x_lvds.v

408 lines
13 KiB
Verilog

// ***************************************************************************
// ***************************************************************************
// 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:
// <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
//
// OR
//
// 2. An ADI specific BSD license, which can be found in the top level directory
// of this repository (LICENSE_ADIBSD), and also on-line at:
// https://github.com/analogdevicesinc/hdl/blob/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_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 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_s;
wire sdi_s;
wire scko_s;
wire sdo_s;
generate
if (XILINX_INTEL_N == 0) begin
assign scki_p = scki_s;
assign scki_n = 1'b0;
assign sdi_p = sdi_s;
assign sdi_n = 1'b0;
assign scko_s = scko_p;
assign sdo_s = sdo_p;
end else begin
OBUFDS obufds_scki (
.O(scki_n),
.OB(scki_p),
.I(scki_s));
OBUFDS obufds_sdi (
.O(sdi_n),
.OB(sdi_p),
.I(sdi_s));
IBUFDS #(
.CCIO_EN_M("TRUE"),
.CCIO_EN_S("TRUE")
) ibufds_scko (
.O(scko_s),
.I(scko_p),
.IB(scko_n));
IBUFDS #(
.CCIO_EN_M("TRUE"),
.CCIO_EN_S("TRUE")
) ibufds_sdo (
.O(sdo_s),
.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_s = scki_i & acquire_data;
// capture data per lane in rx buffer on every edge of scko_s
// ignore when busy forced scko_s to 0
always @(posedge clk) begin
scko_d <= scko_s;
sdo_d <= sdo_s;
if (scko_s != scko_d && scki_s != 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
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
// ch_data_lock - locks all the channel, 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_s
always @(negedge clk) begin
if (start_transfer_s || rst) begin
sdi_index <= 5'd23;
end else begin
if (scki_s != 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_s = (sdi_index != 5'b11111)? softspan_next_int[sdi_index] : 1'b0;
endmodule