pluto_hdl_adi/library/axi_ltc235x/axi_ltc235x_cmos.v

530 lines
18 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_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 corresponds to which lane, e.g. lane_0_ch stores the current channel lane 0 has
// ch_data_lock[i] - locks ch 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
// dont 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