axi_ad4858: Initial commit

The axi_ad4858 IP core is design as the HDL interface for the AD4858 ADC.
Features:
 - AXI based configuration
 - LVDS and CMOS support
 - Configurable number of active data lines (CMOS - build-time configurable)
 - Oversampling support
 - Supports packet formats 0,1,2 or 3
 - CRC check support
 - Real-time data header access
 - Channel based raw data access(0x0408)
 - Xilinx devices compatible

Documentation at https://wiki.analog.com/resources/fpga/docs/axi_ad4858
main
AndreiGrozav 2023-02-13 19:42:15 +02:00 committed by AndreiGrozav
parent 6128dd1ab5
commit f8ee407f34
9 changed files with 2306 additions and 5 deletions

View File

@ -1,10 +1,10 @@
################################################################################ ####################################################################################
################################################################################ ####################################################################################
## Copyright (C) 2018-2023 Analog Devices, Inc. ## Copyright (c) 2018 - 2023 Analog Devices, Inc.
### SPDX short identifier: BSD-1-Clause ### SPDX short identifier: BSD-1-Clause
## Auto-generated, do not modify! ## Auto-generated, do not modify!
################################################################################ ####################################################################################
################################################################################ ####################################################################################
include ../quiet.mk include ../quiet.mk
@ -15,6 +15,7 @@ all: lib
clean: clean:
$(MAKE) -C ad463x_data_capture clean $(MAKE) -C ad463x_data_capture clean
$(MAKE) -C axi_ad3552r clean $(MAKE) -C axi_ad3552r clean
$(MAKE) -C axi_ad4858 clean
$(MAKE) -C axi_ad5766 clean $(MAKE) -C axi_ad5766 clean
$(MAKE) -C axi_ad7606x clean $(MAKE) -C axi_ad7606x clean
$(MAKE) -C axi_ad7616 clean $(MAKE) -C axi_ad7616 clean
@ -139,6 +140,7 @@ clean-all:clean
lib: lib:
$(MAKE) -C ad463x_data_capture $(MAKE) -C ad463x_data_capture
$(MAKE) -C axi_ad3552r $(MAKE) -C axi_ad3552r
$(MAKE) -C axi_ad4858
$(MAKE) -C axi_ad5766 $(MAKE) -C axi_ad5766
$(MAKE) -C axi_ad7606x $(MAKE) -C axi_ad7606x
$(MAKE) -C axi_ad7616 $(MAKE) -C axi_ad7616

View File

@ -0,0 +1,35 @@
####################################################################################
## Copyright (c) 2018 - 2023 Analog Devices, Inc.
### SPDX short identifier: BSD-1-Clause
## Auto-generated, do not modify!
####################################################################################
LIBRARY_NAME := axi_ad4858
GENERIC_DEPS += ../common/ad_datafmt.v
GENERIC_DEPS += ../common/ad_edge_detect.v
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_delay_cntrl.v
GENERIC_DEPS += ../common/up_xfer_cntrl.v
GENERIC_DEPS += ../common/up_xfer_status.v
GENERIC_DEPS += axi_ad4858.v
GENERIC_DEPS += axi_ad4858_channel.v
GENERIC_DEPS += axi_ad4858_cmos.v
GENERIC_DEPS += axi_ad4858_crc.v
GENERIC_DEPS += axi_ad4858_lvds.v
XILINX_DEPS += ../util_cdc/sync_bits.v
XILINX_DEPS += ../xilinx/common/ad_data_in.v
XILINX_DEPS += ../xilinx/common/ad_rst_constr.xdc
XILINX_DEPS += ../xilinx/common/ad_serdes_out.v
XILINX_DEPS += ../xilinx/common/up_clock_mon_constr.xdc
XILINX_DEPS += ../xilinx/common/up_xfer_cntrl_constr.xdc
XILINX_DEPS += ../xilinx/common/up_xfer_status_constr.xdc
XILINX_DEPS += axi_ad4858_constr.ttcl
XILINX_DEPS += axi_ad4858_ip.tcl
include ../scripts/library.mk

View File

@ -0,0 +1,539 @@
// ***************************************************************************
// ***************************************************************************
// 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/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_ad4858 #(
parameter FPGA_TECHNOLOGY = 0,
parameter DELAY_REFCLK_FREQ = 200,
parameter IODELAY_ENABLE = 1,
parameter ID = 0,
parameter LVDS_CMOS_N = 1,
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 ECHO_CLK_EN = 1,
parameter EXTERNAL_CLK = 1
) (
// clocks
input delay_clk,
input external_clk,
input external_fast_clk,
// physical data interface
input cnvs,
input busy,
output lvds_cmos_n,
// cmos if signals
output scki,
input scko,
input lane_0,
input lane_1,
input lane_2,
input lane_3,
input lane_4,
input lane_5,
input lane_6,
input lane_7,
// lvds if signals
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,
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 [ 0:0] READ_RAW = 1'b1;
localparam CONFIG = {18'd0, READ_RAW, 5'd0, ~LVDS_CMOS_N[0], 7'd0};
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};
// internal registers
reg up_wack = 1'b0;
reg up_rack = 1'b0;
reg [31:0] up_rdata = 32'b0;
reg up_status_or = 1'b0;
reg [ 7:0] adc_reset;
reg adc_if_reset;
// internal signals
wire adc_rst_s;
wire adc_clk_s;
wire scko_s;
wire scko_s_p;
wire scko_s_n;
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 [31:0] up_wdata_s;
wire [31:0] up_rdata_s[0:9];
wire [ 9:0] up_rack_s;
wire [ 9:0] up_wack_s;
wire [255:0] adc_data_if_s;
wire [ 7:0] adc_enable_s;
wire [31:0] adc_data_s[7:0];
wire adc_valid_if;
wire [ 7:0] adc_valid_s;
wire crc_error;
wire [ 7:0] adc_or;
wire [ 7:0] up_adc_or_s;
wire [ 7:0] up_adc_pn_err;
wire [ 7:0] up_adc_pn_oos;
wire [ 7:0] adc_custom_control;
wire [ 1:0] packet_format;
wire oversampling_en;
wire adc_crc_enable_s;
wire delay_rst;
wire delay_locked;
// defaults
assign up_clk = s_axi_aclk;
assign up_rstn = s_axi_aresetn;
assign lvds_cmos_n = LVDS_CMOS_N[0];
assign adc_enable_0 = adc_enable_s[0];
assign adc_enable_1 = adc_enable_s[1];
assign adc_enable_2 = adc_enable_s[2];
assign adc_enable_3 = adc_enable_s[3];
assign adc_enable_4 = adc_enable_s[4];
assign adc_enable_5 = adc_enable_s[5];
assign adc_enable_6 = adc_enable_s[6];
assign adc_enable_7 = adc_enable_s[7];
assign adc_valid = adc_valid_s[0];
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 packet_format = adc_custom_control[1:0];
assign oversampling_en = adc_custom_control[2];
// processor read interface
always @(negedge up_rstn or posedge up_clk) begin
if (up_rstn == 0) begin
up_status_or <= 'd0;
up_wack <= 'd0;
up_rack <= 'd0;
up_rdata <= 'd0;
end else begin
up_status_or <= | up_adc_or_s;
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] |
up_rdata_s[9];
end
end
genvar i;
generate
if (EXTERNAL_CLK == 1'b1) begin
assign adc_clk_s = external_clk;
end else begin
assign adc_clk_s = up_clk;
end
always @(posedge adc_clk_s) begin
adc_if_reset <= adc_rst_s;
end
if (LVDS_CMOS_N == 1) begin // LVDS
wire up_dld;
wire [ 4:0] up_dwdata;
wire [ 4:0] up_drdata;
assign scki = 1'b0;
if (ECHO_CLK_EN == 1'b1) begin
assign scko_s_p = scko_p;
assign scko_s_n = scko_n;
end else begin
assign scko_s_p = scki_p;
assign scko_s_n = scki_n;
end
axi_ad4858_lvds #(
.FPGA_TECHNOLOGY (FPGA_TECHNOLOGY),
.DELAY_REFCLK_FREQ(DELAY_REFCLK_FREQ),
.IODELAY_ENABLE (IODELAY_ENABLE)
) i_ad4858_lvds_interface (
.rst (adc_if_reset),
.clk (adc_clk_s),
.fast_clk (external_fast_clk),
.adc_enable (adc_enable_s),
.adc_crc_enable (adc_crc_enable_s),
.packet_format_in (packet_format),
.oversampling_en (oversampling_en),
.scki_p (scki_p),
.scki_n (scki_n),
.scko_p (scko_s_p),
.scko_n (scko_s_n),
.sdo_p (sdo_p),
.sdo_n (sdo_n),
.busy (busy),
.cnvs (cnvs),
.adc_data (adc_data_if_s),
.adc_valid (adc_valid_if),
.crc_error (crc_error),
.dev_status (),
.up_clk (up_clk),
.up_adc_dld (up_dld),
.up_adc_dwdata (up_dwdata),
.up_adc_drdata (up_drdata),
.delay_clk (delay_clk),
.delay_rst (delay_rst),
.delay_locked (delay_locked));
up_delay_cntrl #(
.DATA_WIDTH(1),
.BASE_ADDRESS(6'h02)
) i_delay_cntrl (
.delay_clk (delay_clk),
.delay_rst (delay_rst),
.delay_locked (delay_locked),
.up_dld (up_dld),
.up_dwdata (up_dwdata),
.up_drdata (up_drdata),
.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[9]),
.up_rreq (up_rreq_s),
.up_raddr (up_raddr_s),
.up_rdata (up_rdata_s[9]),
.up_rack (up_rack_s[9]));
end else begin // CMOS
wire [ 7:0] up_dld;
wire [39:0] up_dwdata;
wire [39:0] up_drdata;
assign scki_p = 1'b0;
assign scki_n = 1'b1;
if (ECHO_CLK_EN == 1'b1) begin
assign scko_s = scko;
end else begin
assign scko_s = scki;
end
axi_ad4858_cmos #(
.FPGA_TECHNOLOGY (FPGA_TECHNOLOGY),
.DELAY_REFCLK_FREQ(DELAY_REFCLK_FREQ),
.IODELAY_ENABLE (IODELAY_ENABLE),
.ACTIVE_LANE (ACTIVE_LANES)
) i_ad4858_cmos_interface (
.rst (adc_if_reset),
.clk (adc_clk_s),
.adc_enable (adc_enable_s),
.adc_crc_enable (adc_crc_enable_s),
.packet_format (packet_format),
.oversampling_en (oversampling_en),
.scki (scki),
.scko (scko_s),
.db_i ({lane_7,
lane_6,
lane_5,
lane_4,
lane_3,
lane_2,
lane_1,
lane_0}),
.busy (busy),
.cnvs (cnvs),
.adc_data (adc_data_if_s),
.adc_valid (adc_valid_if),
.crc_error (crc_error),
.dev_status (),
.up_clk (up_clk),
.up_adc_dld (up_dld),
.up_adc_dwdata (up_dwdata),
.up_adc_drdata (up_drdata),
.delay_clk (delay_clk),
.delay_rst (delay_rst),
.delay_locked (delay_locked));
up_delay_cntrl #(
.DATA_WIDTH(8),
.BASE_ADDRESS(6'h02)
) i_delay_cntrl (
.delay_clk (delay_clk),
.delay_rst (delay_rst),
.delay_locked (delay_locked),
.up_dld (up_dld),
.up_dwdata (up_dwdata),
.up_drdata (up_drdata),
.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[9]),
.up_rreq (up_rreq_s),
.up_raddr (up_raddr_s),
.up_rdata (up_rdata_s[9]),
.up_rack (up_rack_s[9]));
end
// adc channels
for (i = 0; i < 8; i=i+1) begin : channel
always @(posedge adc_clk_s) begin
adc_reset[i] <= adc_rst_s;
end
axi_ad4858_channel #(
.CHANNEL_ID(i),
.ACTIVE_LANE (ACTIVE_LANES)
) i_adc_channel (
.adc_clk (adc_clk_s),
.adc_rst (adc_reset[i]),
.adc_ch_valid_in (adc_valid_if),
.adc_ch_data_in (adc_data_if_s[32*i+:32]),
.if_crc_err (crc_error),
.adc_enable (adc_enable_s[i]),
.adc_valid (adc_valid_s[i]),
.adc_data (adc_data_s[i]),
.adc_or (adc_or[i]),
.adc_status_header (),
.packet_format (packet_format),
.oversampling_en (oversampling_en),
.up_adc_or (up_adc_or_s[i]),
.up_adc_pn_err (up_adc_pn_err[i]),
.up_adc_pn_oos (up_adc_pn_oos[i]),
.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
// adc up 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_custom_control (adc_custom_control),
.adc_sdr_ddr_n (),
.adc_symb_op (),
.adc_symb_8_16b (),
.adc_num_lanes (),
.adc_crc_enable (adc_crc_enable_s),
.up_pps_rcounter (32'b0),
.up_pps_status (1'b0),
.up_pps_irq_mask (),
.up_adc_ce (),
.up_status_pn_err (|up_adc_pn_err),
.up_status_pn_oos (|up_adc_pn_oos),
.up_status_or (|up_status_or),
.up_adc_r1_mode(),
.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 ('d0),
.adc_ctrl_status ('d0),
.up_usr_chanmax_out (),
.up_usr_chanmax_in (8'd8),
.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

View File

@ -0,0 +1,275 @@
// ***************************************************************************
// ***************************************************************************
// 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/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_ad4858_channel #(
parameter CHANNEL_ID = 0,
parameter ACTIVE_LANE = 8'b11111111, // for cmos if
parameter USERPORTS_DISABLE = 0,
parameter DATAFORMAT_DISABLE = 0
) (
// adc interface
input adc_clk,
input adc_rst,
input adc_ch_valid_in,
input [31:0] adc_ch_data_in,
input if_crc_err,
// dma interface
output adc_enable,
output reg adc_valid,
output reg [31:0] adc_data,
// error monitoring
output reg adc_or,
output reg [ 6:0] adc_status_header,
output up_adc_pn_err,
output up_adc_pn_oos,
output up_adc_or,
// format
input [ 1:0] packet_format,
input oversampling_en,
// processor interface
input up_rstn,
input up_clk,
input up_wreq,
input [13:0] up_waddr,
input [31:0] up_wdata,
output up_wack,
input up_rreq,
input [13:0] up_raddr,
output [31:0] up_rdata,
output up_rack
);
// internal registers
reg adc_pn_err;
reg adc_valid_f1;
reg [23:0] adc_data_f1;
reg [31:0] pattern;
reg [31:0] adc_raw_data;
reg [31:0] read_channel_data;
reg [31:0] expected_package_pattern;
// internal signals
wire to_be_checked;
wire adc_dfmt_se_s;
wire adc_dfmt_type_s;
wire adc_dfmt_enable_s;
wire adc_pn_err_s;
wire adc_valid_f2;
wire [31:0] adc_data_f2;
wire adc_valid_f2_ovs;
wire [31:0] adc_data_f2_ovs;
wire [31:0] expected_pattern;
wire [31:0] expected_package_pattern_s;
// expected pattern
assign expected_pattern = {CHANNEL_ID[3:0], 28'hace3c2a};
assign expected_package_pattern_s = packet_format == 2'd0 ? expected_pattern >> 12:
packet_format == 2'd1 ? expected_pattern >> 8:
packet_format == 2'd2 ? expected_pattern : expected_pattern;
// the pattern check result is masked out for unused lanes(cmos)
assign to_be_checked = ACTIVE_LANE[CHANNEL_ID[3:0]] ? 1'b1 : 1'b0;
always @(posedge adc_clk) begin
expected_package_pattern <= expected_package_pattern_s;
if (expected_package_pattern == pattern) begin
adc_pn_err <= 1'b0;
end else begin
adc_pn_err <= to_be_checked;
end
end
// AD4858 is a 20 bit resolution ADC, when oversampling is enabled the data
// resolutions growes to 24
always @(posedge adc_clk) begin
adc_valid_f1 <= adc_ch_valid_in;
case ({packet_format,oversampling_en})
3'h0,3'h1: begin // packet format 20 - oversampling on/off
adc_raw_data <= {12'd0,adc_ch_data_in[19:0]};
adc_data_f1 <= {4'd0, adc_ch_data_in[19:0]};
adc_or <= 1'b0;
adc_status_header <= 7'd0;
pattern <= {12'd0, adc_ch_data_in[19:0]};
end
3'h2: begin // packet format 24 - oversampling off
adc_raw_data <= {12'd0,adc_ch_data_in[23:4]};
adc_data_f1 <= {4'd0, adc_ch_data_in[23:4]};
adc_or <= adc_ch_data_in[3];
adc_status_header <= {adc_ch_data_in[2:0], 4'd0};
pattern <= {8'd0, adc_ch_data_in[23:0]};
end
3'h3: begin // packet format 24 - oversampling on
adc_raw_data <= {8'd0,adc_ch_data_in[23:0]};
adc_data_f1 <= adc_ch_data_in[23:0];
adc_or <= 3'd0;
adc_status_header <= 7'd0;
pattern <= {8'd0, adc_ch_data_in[23:0]};
end
3'h4,3'h6: begin // packet format 32 - oversampling off
adc_raw_data <= {12'd0, adc_ch_data_in[31:12]};
adc_data_f1 <= {4'd0, adc_ch_data_in[31:12]};
adc_or <= adc_ch_data_in[11];
adc_status_header <= adc_ch_data_in[10:4];
pattern <= adc_ch_data_in;
end
3'h5,3'h7: begin // packet format 32 - oversampling on
adc_raw_data <= {8'd0, adc_ch_data_in[31:8]};
adc_data_f1 <= adc_ch_data_in[31:8];
adc_or <= adc_ch_data_in[7];
adc_status_header <= adc_ch_data_in[6:0];
pattern <= adc_ch_data_in;
end
endcase
end
ad_datafmt #(
.DATA_WIDTH (20),
.BITS_PER_SAMPLE (32),
.DISABLE (DATAFORMAT_DISABLE)
) i_ad_datafmt (
.clk (adc_clk),
.valid (adc_valid_f1),
.data (adc_data_f1[19:0]),
.valid_out (adc_valid_f2),
.data_out (adc_data_f2),
.dfmt_enable (adc_dfmt_enable_s),
.dfmt_type (adc_dfmt_type_s),
.dfmt_se (adc_dfmt_se_s));
ad_datafmt #(
.DATA_WIDTH (24),
.BITS_PER_SAMPLE (32),
.DISABLE (DATAFORMAT_DISABLE)
) i_ad_datafmt_oversampling (
.clk (adc_clk),
.valid (adc_valid_f1),
.data (adc_data_f1),
.valid_out (adc_valid_f2_ovs),
.data_out (adc_data_f2_ovs),
.dfmt_enable (adc_dfmt_enable_s),
.dfmt_type (adc_dfmt_type_s),
.dfmt_se (adc_dfmt_se_s));
always @(posedge adc_clk) begin
adc_data <= (packet_format != 'd0 && oversampling_en) ? adc_data_f2_ovs : adc_data_f2;
adc_valid <= (packet_format != 'd0 && oversampling_en) ? adc_valid_f2_ovs : adc_valid_f2;
end
always @(posedge adc_clk) begin
if (adc_valid_f1) begin
read_channel_data <= adc_raw_data;
end else begin
read_channel_data <= read_channel_data;
end
end
// adc channel regmap
up_adc_channel #(
.CHANNEL_ID (CHANNEL_ID),
.USERPORTS_DISABLE (USERPORTS_DISABLE),
.DATAFORMAT_DISABLE (DATAFORMAT_DISABLE),
.DCFILTER_DISABLE (1'b1),
.IQCORRECTION_DISABLE (1'b1)
) i_up_adc_channel (
.adc_clk (adc_clk),
.adc_rst (adc_rst),
.adc_enable (adc_enable),
.adc_iqcor_enb (),
.adc_dcfilt_enb (),
.adc_dfmt_se (adc_dfmt_se_s),
.adc_dfmt_type (adc_dfmt_type_s),
.adc_dfmt_enable (adc_dfmt_enable_s),
.adc_dcfilt_offset (),
.adc_dcfilt_coeff (),
.adc_iqcor_coeff_1 (),
.adc_iqcor_coeff_2 (),
.adc_pnseq_sel (),
.adc_data_sel (),
.adc_pn_err (adc_pn_err),
.adc_pn_oos (1'b0),
.adc_or (1'b0),
.adc_read_data (read_channel_data),
.adc_status_header ({1'b1, adc_status_header}),
.adc_crc_err (if_crc_err),
.up_adc_crc_err (),
.up_adc_pn_err (up_adc_pn_err),
.up_adc_pn_oos (up_adc_pn_oos),
.up_adc_or (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'd20),
.adc_usr_decimation_m (16'd1),
.adc_usr_decimation_n (16'd1),
.up_rstn (up_rstn),
.up_clk (up_clk),
.up_wreq (up_wreq),
.up_waddr (up_waddr),
.up_wdata (up_wdata),
.up_wack (up_wack),
.up_rreq (up_rreq),
.up_raddr (up_raddr),
.up_rdata (up_rdata),
.up_rack (up_rack));
endmodule

View File

@ -0,0 +1,659 @@
// ***************************************************************************
// ***************************************************************************
// 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/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_ad4858_cmos #(
parameter FPGA_TECHNOLOGY = 0,
parameter DELAY_REFCLK_FREQ = 200,
parameter IODELAY_ENABLE = 1,
parameter IODELAY_GROUP = "dev_if_delay_group",
parameter ACTIVE_LANE = 8'b11111111
) (
input rst,
input clk,
input [ 7:0] adc_enable,
input adc_crc_enable,
// physical interface
output scki,
input scko,
input [ 7:0] db_i,
input busy,
input cnvs,
// format
input [ 1:0] packet_format,
input oversampling_en,
// channel interface
output [255:0] adc_data,
output reg adc_valid,
output reg crc_error,
output [ 7:0] dev_status,
// delay interface (for IDELAY macros)
input up_clk,
input [ 7:0] up_adc_dld,
input [39:0] up_adc_dwdata,
output [39:0] up_adc_drdata,
input delay_clk,
input delay_rst,
output delay_locked
);
localparam NEG_EDGE = 1;
localparam DW = 32;
localparam BW = DW - 1;
localparam PACKET_1_BITS = 20;
localparam PACKET_2_BITS = 24;
localparam PACKET_3_BITS = 32;
// internal registers
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 [ 5:0] data_counter = 6'h0;
reg [ 5:0] scki_counter = 6'h0;
reg scki_i;
reg scki_d;
reg [BW:0] adc_data_store[8:0];
reg [BW:0] adc_data_init[7:0];
reg adc_valid_init;
reg adc_valid_init_d;
reg [ 8:0] ch_capture;
reg [ 8:0] ch_captured;
reg [ 4:0] adc_ch0_shift;
reg [ 4:0] adc_ch1_shift;
reg [ 4:0] adc_ch2_shift;
reg [ 4:0] adc_ch3_shift;
reg [ 4:0] adc_ch4_shift;
reg [ 4:0] adc_ch5_shift;
reg [ 4:0] adc_ch6_shift;
reg [ 4:0] adc_ch7_shift;
reg [ 4:0] adc_ch0_shift_d;
reg [ 4:0] adc_ch1_shift_d;
reg [ 4:0] adc_ch2_shift_d;
reg [ 4:0] adc_ch3_shift_d;
reg [ 4:0] adc_ch4_shift_d;
reg [ 4:0] adc_ch5_shift_d;
reg [ 4:0] adc_ch6_shift_d;
reg [ 4:0] adc_ch7_shift_d;
reg [ 3:0] lane_0_data = 'd0;
reg [ 3:0] lane_1_data = 'd0;
reg [ 3:0] lane_2_data = 'd0;
reg [ 3:0] lane_3_data = 'd0;
reg [ 3:0] lane_4_data = 'd0;
reg [ 3:0] lane_5_data = 'd0;
reg [ 3:0] lane_6_data = 'd0;
reg [ 3:0] lane_7_data = 'd0;
reg [ 8:0] ch_data_lock = 'h1ff;
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 start_transfer;
reg crc_enable_window;
reg run_crc;
reg run_crc_d;
reg [288:0] crc_data_in;
reg [15:0] crc_cnt;
reg [ 7:0] data_in_byte;
// internal wires
wire [ 7:0] db_s;
wire [31:0] status_and_crc_data;
wire aquire_data;
wire scki_cnt_rst;
wire adc_cnvs_redge;
wire conversion_completed;
wire conversion_quiet_time_s;
wire [ 5:0] packet_lenght;
wire crc_reset;
wire crc_enable;
wire crc_valid;
wire [15:0] crc_res;
wire [15:0] crc_data_lenght;
// packet format selection
assign packet_lenght = packet_format == 2'd0 ? 6'd20 :
packet_format == 2'd1 ? 6'd24 : 6'd32;
always @(posedge clk) begin
if (rst == 1'b1) begin
busy_m1 <= 1'b0;
busy_m2 <= 1'b0;
start_transfer <= 1'b0;
end else begin
busy_m1 <= busy;
busy_m2 <= busy_m1;
start_transfer <= busy_m2 & !busy_m1;
end
end
always @(posedge clk) begin
if (start_transfer) begin
crc_enable_window <= adc_crc_enable;
end
end
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 (aquire_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 + 1;
scki_i <= 1'b1;
end else if (conversion_quiet_time_s == 1'b1) begin
scki_counter <= scki_counter;
scki_i <= scki_i;
end else begin
scki_counter <= scki_counter;
scki_i <= ~scki_i;
end
end
end
// busy period counter
always @(posedge clk) begin
if (rst == 1'b1) begin
run_busy_period_cnt <= 1'b0;
busy_conversion_cnt <= 'd0;
busy_measure_value <= 'd0;
end else 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
if (adc_cnvs_redge == 1'b1) begin
busy_conversion_cnt <= 'd0;
end else if (start_transfer == 1'b1) begin
busy_measure_value <= busy_conversion_cnt;
end else if (run_busy_period_cnt == 1'b1) begin
busy_conversion_cnt <= busy_conversion_cnt + 'd1;
end
end
end
always @(posedge clk) begin
if (rst) begin
period_cnt <= 'd0;
cnvs_d <= 'd0;
conversion_quiet_time <= 1'b0;
end else 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
end
assign conversion_quiet_time_s = (oversampling_en == 1) ? conversion_quiet_time | cnvs : 1'b0;
assign conversion_completed = (period_cnt == busy_measure_value) ? 1'b1 : 1'b0;
assign adc_cnvs_redge = ~cnvs_d & cnvs;
assign scki_cnt_rst = (scki_counter == packet_lenght) ? 1'b1 : 1'b0;
assign scki = scki_i | ~aquire_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 activated if the reading process 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.
*/
always @(posedge clk) begin
if (start_transfer) begin
lane_0_data <= 4'd0;
lane_1_data <= 4'd1;
lane_2_data <= 4'd2;
lane_3_data <= 4'd3;
lane_4_data <= 4'd4;
lane_5_data <= 4'd5;
lane_6_data <= 4'd6;
lane_7_data <= 4'd7;
ch_data_lock <= 9'd0;
end else if (aquire_data == 1'b1 && (scki_cnt_rst & (~scki_d & scki_i))) begin
lane_0_data <= lane_0_data[3] == 1'b1 ? 4'd0 : lane_0_data + 1;
lane_1_data <= lane_1_data[3] == 1'b1 ? 4'd0 : lane_1_data + 1;
lane_2_data <= lane_2_data[3] == 1'b1 ? 4'd0 : lane_2_data + 1;
lane_3_data <= lane_3_data[3] == 1'b1 ? 4'd0 : lane_3_data + 1;
lane_4_data <= lane_4_data[3] == 1'b1 ? 4'd0 : lane_4_data + 1;
lane_5_data <= lane_5_data[3] == 1'b1 ? 4'd0 : lane_5_data + 1;
lane_6_data <= lane_6_data[3] == 1'b1 ? 4'd0 : lane_6_data + 1;
lane_7_data <= lane_7_data[3] == 1'b1 ? 4'd0 : lane_7_data + 1;
ch_data_lock[lane_0_data[3:0]] <= ACTIVE_LANE[0] ? 1'b1 : ch_data_lock[lane_0_data[2:0]];
ch_data_lock[lane_1_data[3:0]] <= ACTIVE_LANE[1] ? 1'b1 : ch_data_lock[lane_1_data[2:0]];
ch_data_lock[lane_2_data[3:0]] <= ACTIVE_LANE[2] ? 1'b1 : ch_data_lock[lane_2_data[2:0]];
ch_data_lock[lane_3_data[3:0]] <= ACTIVE_LANE[3] ? 1'b1 : ch_data_lock[lane_3_data[2:0]];
ch_data_lock[lane_4_data[3:0]] <= ACTIVE_LANE[4] ? 1'b1 : ch_data_lock[lane_4_data[2:0]];
ch_data_lock[lane_5_data[3:0]] <= ACTIVE_LANE[5] ? 1'b1 : ch_data_lock[lane_5_data[2:0]];
ch_data_lock[lane_6_data[3:0]] <= ACTIVE_LANE[6] ? 1'b1 : ch_data_lock[lane_6_data[2:0]];
ch_data_lock[lane_7_data[3:0]] <= ACTIVE_LANE[7] ? 1'b1 : ch_data_lock[lane_7_data[2:0]];
end else begin
lane_0_data <= lane_0_data;
lane_1_data <= lane_1_data;
lane_2_data <= lane_2_data;
lane_3_data <= lane_3_data;
lane_4_data <= lane_4_data;
lane_5_data <= lane_5_data;
lane_6_data <= lane_6_data;
lane_7_data <= lane_7_data;
ch_data_lock <= ch_data_lock;
end
end
assign aquire_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]) &
(ch_data_lock[8] | ~crc_enable_window));
// capture data
genvar i;
generate
if (IODELAY_ENABLE == 1) begin
ad_data_in #(
.SINGLE_ENDED (1),
.DDR_SDR_N (0),
.FPGA_TECHNOLOGY (FPGA_TECHNOLOGY),
.IODELAY_CTRL (1),
.IODELAY_ENABLE (IODELAY_ENABLE),
.IODELAY_GROUP (IODELAY_GROUP),
.REFCLK_FREQUENCY (DELAY_REFCLK_FREQ)
) i_rx_data (
.rx_clk (scko),
.rx_data_in_p (db_i[0]),
.rx_data_in_n (1'd0),
.rx_data_p (db_s[0]),
.rx_data_n (),
.up_clk (up_clk),
.up_dld (up_adc_dld[0]),
.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));
end
for (i = 1; i < 8; i = i + 1) begin: g_rx_lanes
ad_data_in #(
.SINGLE_ENDED (1),
.DDR_SDR_N (0),
.FPGA_TECHNOLOGY (FPGA_TECHNOLOGY),
.IODELAY_CTRL (0),
.IODELAY_ENABLE (IODELAY_ENABLE),
.IODELAY_GROUP (IODELAY_GROUP),
.REFCLK_FREQUENCY (DELAY_REFCLK_FREQ)
) i_rx_data (
.rx_clk (scko),
.rx_data_in_p (db_i[i]),
.rx_data_in_n (1'd0),
.rx_data_p (db_s[i]),
.rx_data_n (),
.up_clk (up_clk),
.up_dld (up_adc_dld[i]),
.up_dwdata (up_adc_dwdata[((i*5)+4):(i*5)]),
.up_drdata (up_adc_drdata[((i*5)+4):(i*5)]),
.delay_clk (delay_clk),
.delay_rst (delay_rst),
.delay_locked ());
end
endgenerate
// the most significant bits of the adc_lane_x will remaind from the
// previous sample for packet formats other than 32.
always @(negedge scko) begin
adc_lane_0 <= {adc_lane_0[BW-1:0], db_s[0]};
adc_lane_1 <= {adc_lane_1[BW-1:0], db_s[1]};
adc_lane_2 <= {adc_lane_2[BW-1:0], db_s[2]};
adc_lane_3 <= {adc_lane_3[BW-1:0], db_s[3]};
adc_lane_4 <= {adc_lane_4[BW-1:0], db_s[4]};
adc_lane_5 <= {adc_lane_5[BW-1:0], db_s[5]};
adc_lane_6 <= {adc_lane_6[BW-1:0], db_s[6]};
adc_lane_7 <= {adc_lane_7[BW-1:0], db_s[7]};
end
always @(posedge clk) begin
if (rst == 1'b1) begin
adc_valid_init <= 1'b0;
end else begin
if (data_counter == packet_lenght && adc_valid_init == 1'b0) begin
adc_valid_init <= 1'b1;
end else begin
adc_valid_init <= 1'b0;
end
end
end
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 <= 'h0;
end else begin
data_counter <= scki_counter;
if (data_counter == packet_lenght) 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 else begin
adc_data_init[0] <= adc_data_init[0];
adc_data_init[1] <= adc_data_init[1];
adc_data_init[2] <= adc_data_init[2];
adc_data_init[3] <= adc_data_init[3];
adc_data_init[4] <= adc_data_init[4];
adc_data_init[5] <= adc_data_init[5];
adc_data_init[6] <= adc_data_init[6];
adc_data_init[7] <= adc_data_init[7];
end
end
end
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 <= 9'd0;
ch_captured <= 9'd0;
adc_ch0_shift <= 4'd0;
adc_ch1_shift <= 4'd0;
adc_ch2_shift <= 4'd0;
adc_ch3_shift <= 4'd0;
adc_ch4_shift <= 4'd0;
adc_ch5_shift <= 4'd0;
adc_ch6_shift <= 4'd0;
adc_ch7_shift <= 4'd0;
adc_ch0_shift_d <= 4'd0;
adc_ch1_shift_d <= 4'd0;
adc_ch2_shift_d <= 4'd0;
adc_ch3_shift_d <= 4'd0;
adc_ch4_shift_d <= 4'd0;
adc_ch5_shift_d <= 4'd0;
adc_ch6_shift_d <= 4'd0;
adc_ch7_shift_d <= 4'd0;
end else begin
ch_capture <= ch_data_lock;
ch_captured <= ch_capture;
adc_valid_init_d <= adc_valid_init;
adc_ch0_shift <= {ACTIVE_LANE[0],lane_0_data};
adc_ch1_shift <= {ACTIVE_LANE[1],lane_1_data};
adc_ch2_shift <= {ACTIVE_LANE[2],lane_2_data};
adc_ch3_shift <= {ACTIVE_LANE[3],lane_3_data};
adc_ch4_shift <= {ACTIVE_LANE[4],lane_4_data};
adc_ch5_shift <= {ACTIVE_LANE[5],lane_5_data};
adc_ch6_shift <= {ACTIVE_LANE[6],lane_6_data};
adc_ch7_shift <= {ACTIVE_LANE[7],lane_7_data};
adc_ch0_shift_d <= adc_ch0_shift;
adc_ch1_shift_d <= adc_ch1_shift;
adc_ch2_shift_d <= adc_ch2_shift;
adc_ch3_shift_d <= adc_ch3_shift;
adc_ch4_shift_d <= adc_ch4_shift;
adc_ch5_shift_d <= adc_ch5_shift;
adc_ch6_shift_d <= adc_ch6_shift;
adc_ch7_shift_d <= adc_ch7_shift;
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]) &
(ch_captured[8] | ~crc_enable_window);
end
end
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;
adc_data_store[8] <= 'd0;
end else begin
if (!adc_valid_init_d & adc_valid_init) begin
if (adc_ch0_shift_d[4] == 1'b1) begin
adc_data_store[adc_ch0_shift_d[3:0]] <= adc_data_init[0];
end
if (adc_ch1_shift_d[4] == 1'b1) begin
adc_data_store[adc_ch1_shift_d[3:0]] <= adc_data_init[1];
end
if (adc_ch2_shift_d[4] == 1'b1) begin
adc_data_store[adc_ch2_shift_d[3:0]] <= adc_data_init[2];
end
if (adc_ch3_shift_d[4] == 1'b1) begin
adc_data_store[adc_ch3_shift_d[3:0]] <= adc_data_init[3];
end
if (adc_ch4_shift_d[4] == 1'b1) begin
adc_data_store[adc_ch4_shift_d[3:0]] <= adc_data_init[4];
end
if (adc_ch5_shift_d[4] == 1'b1) begin
adc_data_store[adc_ch5_shift_d[3:0]] <= adc_data_init[5];
end
if (adc_ch6_shift_d[4] == 1'b1) begin
adc_data_store[adc_ch6_shift_d[3:0]] <= adc_data_init[6];
end
if (adc_ch7_shift_d[4] == 1'b1) begin
adc_data_store[adc_ch7_shift_d[3:0]] <= adc_data_init[7];
end
end
end
end
assign dev_status = packet_format == 0 ? adc_data_store[8][23:16] << 4 :
adc_data_store[8][23:16];
assign crc_data = adc_data_store[8][15:0];
assign adc_data = {adc_data_store[7],
adc_data_store[6],
adc_data_store[5],
adc_data_store[4],
adc_data_store[3],
adc_data_store[2],
adc_data_store[1],
adc_data_store[0]};
// CRC checker logic
always @(posedge clk) begin
if (rst == 1) begin
crc_data_in <= 0;
end else begin
if (adc_valid == 1) begin
if (packet_format == 0) begin
crc_data_in <= {104'd0,
adc_data_store[0][19:0],
adc_data_store[1][19:0],
adc_data_store[2][19:0],
adc_data_store[3][19:0],
adc_data_store[4][19:0],
adc_data_store[5][19:0],
adc_data_store[6][19:0],
adc_data_store[7][19:0],
adc_data_store[8][19:0],
4'd0};
end else if (packet_format == 1) begin
crc_data_in <= {72'd0,
adc_data_store[0][23:0],
adc_data_store[1][23:0],
adc_data_store[2][23:0],
adc_data_store[3][23:0],
adc_data_store[4][23:0],
adc_data_store[5][23:0],
adc_data_store[6][23:0],
adc_data_store[7][23:0],
adc_data_store[8][23:0]};
end else begin
crc_data_in <= {adc_data_store[0],
adc_data_store[1],
adc_data_store[2],
adc_data_store[3],
adc_data_store[4],
adc_data_store[5],
adc_data_store[6],
adc_data_store[7],
adc_data_store[8]};
end
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.
// First extra 4 bits entering the checker being 0, will not affect the result
// since the initial value of the LFSR is 0x0000.
assign crc_data_lenght = packet_format == 2'd0 ? 16'd176 : // 184-8
packet_format == 2'd1 ? 16'd208 : 16'd280;
always @(posedge clk) begin
if (crc_enable_window == 0 || adc_valid == 1) begin
crc_cnt <= crc_data_lenght;
data_in_byte <= 0;
run_crc <= adc_valid;
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
data_in_byte <= crc_data_in[crc_cnt +: 8];
end else begin
end
end
// the counter is initialized with n-1 to accommodate the byte shifter
run_crc_d <= run_crc;
end
assign crc_reset = adc_valid;
assign crc_enable = run_crc | run_crc_d;
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));
endmodule

View File

@ -0,0 +1,29 @@
###############################################################################
## Copyright (C) 2023 Analog Devices, Inc. All rights reserved.
### SPDX short identifier: ADIBSD
###############################################################################
<: set ComponentName [getComponentNameString] :>
<: setOutputDirectory "./" :>
<: setFileName [ttcl_add $ComponentName "_constr"] :>
<: setFileExtension ".xdc" :>
<: setFileProcessingOrder late :>
<: set lvds_cmos_n [getBooleanValue "LVDS_CMOS_N"] :>
set up_clk [get_clocks -of_objects [get_ports s_axi_aclk]]
<: if {$lvds_cmos_n} { :>
set_false_path -quiet \
-from $up_clk \
-to [get_cells -quiet -hier -filter {name =~ *packet_format_reg* && IS_SEQUENTIAL}]
set_false_path -quiet \
-from $up_clk \
-to [get_cells -quiet -hier -filter {name =~ *ch_*_base_reg* && IS_SEQUENTIAL}]
set_false_path -quiet \
-from [get_cells -hier -filter {name =~ *packet_format_reg* && IS_SEQUENTIAL}] \
-to [get_cells -quiet -hieri -filter {name =~ *packet_cnt_length_reg* && IS_SEQUENTIAL}]
<: } :>

View File

@ -0,0 +1,83 @@
// ***************************************************************************
// ***************************************************************************
// 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/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.
//
// ***************************************************************************
// ***************************************************************************
// CRC polynomial 0x755B = x^16 + x^14 + x^13 + x^12 + x^10 + x^8 + x^6 + x^4 + x^3 + x + 1
// CRC width: 16 bits
// Input word width: 8 bits
// Initial value: 0x0000
// Direction: shift left
module axi_ad4858_crc (
input rst,
input clk,
input crc_en,
input [ 7:0] d_in,
output crc_valid,
output [15:0] crc_res
);
reg [15:0] lfsr;
reg [15:0] crc_en_d;
always @(posedge clk) begin
if (rst == 1'b1) begin
lfsr <= 16'd0;
crc_en_d <= 1'b0;
end else begin
crc_en_d <= crc_en;
if (crc_en == 1'b1) begin
lfsr[0] <= lfsr[8] ^ lfsr[10] ^ lfsr[11] ^ lfsr[14] ^ lfsr[15] ^ d_in[0] ^ d_in[2] ^ d_in[3] ^ d_in[6] ^ d_in[7];
lfsr[1] <= lfsr[8] ^ lfsr[9] ^ lfsr[10] ^ lfsr[12] ^ lfsr[14] ^ d_in[0] ^ d_in[1] ^ d_in[2] ^ d_in[4] ^ d_in[6];
lfsr[2] <= lfsr[9] ^ lfsr[10] ^ lfsr[11] ^ lfsr[13] ^ lfsr[15] ^ d_in[1] ^ d_in[2] ^ d_in[3] ^ d_in[5] ^ d_in[7];
lfsr[3] <= lfsr[8] ^ lfsr[12] ^ lfsr[15] ^ d_in[0] ^ d_in[4] ^ d_in[7];
lfsr[4] <= lfsr[8] ^ lfsr[9] ^ lfsr[10] ^ lfsr[11] ^ lfsr[13] ^ lfsr[14] ^ lfsr[15] ^ d_in[0] ^ d_in[1] ^ d_in[2] ^ d_in[3] ^ d_in[5] ^ d_in[6] ^ d_in[7];
lfsr[5] <= lfsr[9] ^ lfsr[10] ^ lfsr[11] ^ lfsr[12] ^ lfsr[14] ^ lfsr[15] ^ d_in[1] ^ d_in[2] ^ d_in[3] ^ d_in[4] ^ d_in[6] ^ d_in[7];
lfsr[6] <= lfsr[8] ^ lfsr[12] ^ lfsr[13] ^ lfsr[14] ^ d_in[0] ^ d_in[4] ^ d_in[5] ^ d_in[6];
lfsr[7] <= lfsr[9] ^ lfsr[13] ^ lfsr[14] ^ lfsr[15] ^ d_in[1] ^ d_in[5] ^ d_in[6] ^ d_in[7];
lfsr[8] <= lfsr[0] ^ lfsr[8] ^ lfsr[11] ^ d_in[0] ^ d_in[3];
lfsr[9] <= lfsr[1] ^ lfsr[9] ^ lfsr[12] ^ d_in[1] ^ d_in[4];
lfsr[10] <= lfsr[2] ^ lfsr[8] ^ lfsr[11] ^ lfsr[13] ^ lfsr[14] ^ lfsr[15] ^ d_in[0] ^ d_in[3] ^ d_in[5] ^ d_in[6] ^ d_in[7];
lfsr[11] <= lfsr[3] ^ lfsr[9] ^ lfsr[12] ^ lfsr[14] ^ lfsr[15] ^ d_in[1] ^ d_in[4] ^ d_in[6] ^ d_in[7];
lfsr[12] <= lfsr[4] ^ lfsr[8] ^ lfsr[11] ^ lfsr[13] ^ lfsr[14] ^ d_in[0] ^ d_in[3] ^ d_in[5] ^ d_in[6];
lfsr[13] <= lfsr[5] ^ lfsr[8] ^ lfsr[9] ^ lfsr[10] ^ lfsr[11] ^ lfsr[12] ^ d_in[0] ^ d_in[1] ^ d_in[2] ^ d_in[3] ^ d_in[4];
lfsr[14] <= lfsr[6] ^ lfsr[8] ^ lfsr[9] ^ lfsr[12] ^ lfsr[13] ^ lfsr[14] ^ lfsr[15] ^ d_in[0] ^ d_in[1] ^ d_in[4] ^ d_in[5] ^ d_in[6] ^ d_in[7];
lfsr[15] <= lfsr[7] ^ lfsr[9] ^ lfsr[10] ^ lfsr[13] ^ lfsr[14] ^ lfsr[15] ^ d_in[1] ^ d_in[2] ^ d_in[5] ^ d_in[6] ^ d_in[7];
end
end
end
assign crc_res = lfsr;
assign crc_valid = !crc_en & crc_en_d;
endmodule

View File

@ -0,0 +1,139 @@
###############################################################################
## Copyright (C) 2023 Analog Devices, Inc. All rights reserved.
### SPDX short identifier: ADIBSD
###############################################################################
# ip
source ../../scripts/adi_env.tcl
source $ad_hdl_dir/library/scripts/adi_ip_xilinx.tcl
global VIVADO_IP_LIBRARY
adi_ip_create axi_ad4858
adi_ip_files axi_ad4858 [list \
"$ad_hdl_dir/library/common/ad_edge_detect.v" \
"$ad_hdl_dir/library/common/ad_datafmt.v" \
"$ad_hdl_dir/library/common/up_axi.v" \
"$ad_hdl_dir/library/common/ad_rst.v" \
"$ad_hdl_dir/library/common/up_adc_common.v" \
"$ad_hdl_dir/library/common/up_adc_channel.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_delay_cntrl.v" \
"$ad_hdl_dir/library/xilinx/common/up_xfer_cntrl_constr.xdc" \
"$ad_hdl_dir/library/xilinx/common/ad_rst_constr.xdc" \
"$ad_hdl_dir/library/xilinx/common/up_xfer_status_constr.xdc" \
"$ad_hdl_dir/library/xilinx/common/up_clock_mon_constr.xdc" \
"$ad_hdl_dir/library/xilinx/common/ad_data_in.v" \
"$ad_hdl_dir/library/xilinx/common/ad_serdes_out.v" \
"$ad_hdl_dir/library/util_cdc/sync_bits.v" \
"axi_ad4858_constr.ttcl" \
"axi_ad4858_cmos.v" \
"axi_ad4858_channel.v" \
"axi_ad4858_crc.v" \
"axi_ad4858_lvds.v" \
"axi_ad4858.v" ]
adi_ip_properties axi_ad4858
set cc [ipx::current_core]
## Customize XGUI layout
set page0 [ipgui::get_pagespec -name "Page 0" -component $cc]
adi_init_bd_tcl
adi_ip_bd axi_ad4858 "bd/bd.tcl"
set_property company_url {https://wiki.analog.com/resources/fpga/docs/axi_ad4858} [ipx::current_core]
ipgui::add_param -name "EXTERNAL_CLK" -component $cc -parent $page0
set_property -dict [list \
"display_name" "EXTERNAL_CLK_EN" \
"tooltip" "External clock for interface logic, must be 2x faster than IF clk" \
"widget" "checkBox" \
] [ipgui::get_guiparamspec -name "EXTERNAL_CLK" -component $cc]
ipgui::add_param -name "ECHO_CLK_EN" -component $cc -parent $page0
set_property -dict [list \
"display_name" "Echoed clock enabled" \
"widget" "checkBox" \
] [ipgui::get_guiparamspec -name "ECHO_CLK_EN" -component $cc]
ipgui::add_param -name "LVDS_CMOS_N" -component $cc -parent $page0
set_property -dict [list \
"value_validation_type" "pairs" \
"value_validation_pairs" { \
"CMOS" "0" \
"LVDS" "1" \
} \
] [ipx::get_user_parameters "LVDS_CMOS_N" -of_objects $cc]
set_property -dict [list \
"display_name" "Interface type" \
"widget" "comboBox" \
] [ipx::get_user_parameters "LVDS_CMOS_N" -of_objects $cc]
for {set i 0} {$i < 8} {incr i} {
ipgui::add_param -name "LANE_${i}_ENABLE" -component $cc -parent $page0
set_property -dict [list \
"display_name" "LANE_${i}_ENABLE" \
"tooltip" "Lane $i is used" \
"widget" "checkBox" \
] [ipgui::get_guiparamspec -name "LANE_${i}_ENABLE" -component $cc]
set_property value true [ipx::get_user_parameters LANE_${i}_ENABLE -of_objects [ipx::current_core]]
set_property value true [ipx::get_hdl_parameters LANE_${i}_ENABLE -of_objects [ipx::current_core]]
set_property enablement_tcl_expr {expr $LVDS_CMOS_N == 0} [ipx::get_user_parameters LANE_${i}_ENABLE -of_objects [ipx::current_core]]
set_property value_format bool [ipx::get_user_parameters LANE_${i}_ENABLE -of_objects [ipx::current_core]]
set_property value_format bool [ipx::get_hdl_parameters LANE_${i}_ENABLE -of_objects [ipx::current_core]]
adi_set_ports_dependency "lane_$i" \
"(spirit:decode(id('MODELPARAM_VALUE.LANE_${i}_ENABLE')) == 1)"
adi_set_ports_dependency "lane_$i" \
"(spirit:decode(id('MODELPARAM_VALUE.LVDS_CMOS_N')) == 0)"
set_property DRIVER_VALUE "0" [ipx::get_ports lane_$i]
}
# CMOS dependency
adi_set_ports_dependency "scki" \
"(spirit:decode(id('MODELPARAM_VALUE.LVDS_CMOS_N')) == 0)"
adi_set_ports_dependency "scko" \
"(spirit:decode(id('MODELPARAM_VALUE.LVDS_CMOS_N')) == 0) and \
(spirit:decode(id('MODELPARAM_VALUE.ECHO_CLK_EN')) = 1)"
# LVDS dependency
adi_set_ports_dependency "scki_p" \
"(spirit:decode(id('MODELPARAM_VALUE.LVDS_CMOS_N')) == 1)"
adi_set_ports_dependency "scki_n" \
"(spirit:decode(id('MODELPARAM_VALUE.LVDS_CMOS_N')) == 1)"
adi_set_ports_dependency "scko_p" \
"(spirit:decode(id('MODELPARAM_VALUE.LVDS_CMOS_N')) == 1) and \
(spirit:decode(id('MODELPARAM_VALUE.ECHO_CLK_EN')) = 1)"
adi_set_ports_dependency "scko_n" \
"(spirit:decode(id('MODELPARAM_VALUE.LVDS_CMOS_N')) == 1) and \
(spirit:decode(id('MODELPARAM_VALUE.ECHO_CLK_EN')) = 1)"
adi_set_ports_dependency "sdo_p" \
"(spirit:decode(id('MODELPARAM_VALUE.LVDS_CMOS_N')) == 1)"
adi_set_ports_dependency "sdo_n" \
"(spirit:decode(id('MODELPARAM_VALUE.LVDS_CMOS_N')) == 1)"
adi_set_ports_dependency "external_clk" \
"(spirit:decode(id('MODELPARAM_VALUE.EXTERNAL_CLK')) = 1)" 0
set_property driver_value 0 [ipx::get_ports -filter "direction==in" -of_objects $cc]
adi_add_auto_fpga_spec_params
## Save the modifications
ipx::create_xgui_files $cc
ipx::save_core [ipx::current_core]

View File

@ -0,0 +1,540 @@
// ***************************************************************************
// ***************************************************************************
// 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/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_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