pluto_hdl_adi/library/axi_ad4858/axi_ad4858_lvds.v

541 lines
17 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_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