Jump to content
  • 0

Eclypse Z7 and ZMOD-ADC1410 connection


tsarquis

Question

I'm trying to get the ADC1410 to work in the Eclypse Z7 FPGA board with a verilog-pure program, making use of the IP Core provided by Digilent (ZmodADC1410_Controller_0).
Up to now, I'am reading trash data from the IP output, so I suppose I've made a mistake in the connections or in the data acquisition or something else.
I've not seen any example of a verilog instantiation of the IP, so please let me know if there's any out there.

For making the connections, I've followed the schematic in the reference manual.

I'm posting my top level design and I'm also attaching the constraint I've used:

`timescale 1ns / 1ps

module top
(
    input   i_reset,            /* onboard button   */
    input   i_clock,            /* 125 MHz onboard  */
    
    output  o_tx,               /* uart output      */
    
    output  syzygy_d_n_0,       /* sc1_ac_l         */
    output  syzygy_d_p_0,       /* sc1_ac_h         */
    output  syzygy_d_n_1,       /* sc2_ac_l         */
    output  syzygy_d_p_1,       /* sc2_ac_h         */
    output  syzygy_d_n_2,       /* sclk_sc          */
    inout   syzygy_d_p_2,       /* sdio_sc          */
    output  syzygy_d_n_3,       /* sc2_gain_l       */
    output  syzygy_d_p_3,       /* sc2_gain_h       */
    input   syzygy_d_n_4,       /* data 2           */
    input   syzygy_d_p_4,       /* data 9           */
    output  syzygy_d_n_5,       /* sc1_gain_l       */
    output  syzygy_d_p_5,       /* sc1_gain_h       */
    input   syzygy_d_n_6,       /* data 4           */
    input   syzygy_d_p_6,       /* data 3           */
    output  syzygy_d_n_7,       /* com_sc_l         */
    output  syzygy_d_p_7,       /* com_sc_h         */
    input   syzygy_s_16,        /* data 5           */
    input   syzygy_s_17,        /* data 8           */
    input   syzygy_s_18,        /* data 6           */
    input   syzygy_s_19,        /* data 10          */
    input   syzygy_s_20,        /* data 7           */
    input   syzygy_s_21,        /* data 11          */
    input   syzygy_s_22,        /* data 1           */
    input   syzygy_s_23,        /* data 12          */
    input   syzygy_s_24,        /* data 0           */
    input   syzygy_s_25,        /* data 13          */
    output  syzygy_s_26,        /* cs_sc1n          */
    output  syzygy_s_27,        /* sync_adc         */
    output  syzygy_c2p_clk_n,   /* adc clock in n   */
    output  syzygy_c2p_clk_p,   /* adc clock in p   */
    input   syzygy_p2c_clk_p,   /* clkout adc       */
    output  syzygy_p2c_clk_n    /* GND              */
);      
    
    /* System */
    wire            clock;
    wire            locked;
        
    /* ADC */
    localparam  ADC_DATA_OUT_SIZE   =   16;
    localparam  ADC_DATA_IN_SIZE    =   14;
    wire                                    adc_init_done;
    wire                                    adc_clock;
    wire    [ ADC_DATA_OUT_SIZE - 1 : 0 ]   adc_data_out_ch1;
    wire    [ ADC_DATA_OUT_SIZE - 1 : 0 ]   adc_data_out_ch2;
    wire    [ ADC_DATA_IN_SIZE  - 1 : 0 ]   adc_data_in;
    wire                                    adc_test_mode;
    wire                                    adc_fifo_empty_ch1;
    wire                                    adc_fifo_empty_ch2;
    integer                                 adc_data_count;
    
    /* Serial */
    localparam  SERIAL_DATA_SIZE    =   8;
    localparam  SERIAL_CLK_COUNT    =   31000000;   
    reg                                     serial_send;
    wire                                    serial_ready;
    reg     [ SERIAL_DATA_SIZE - 1 : 0 ]    serial_data_l;
    reg     [ SERIAL_DATA_SIZE - 1 : 0 ]    serial_data_h;
    integer                                 clk_counter;
    
    /* UART: send one convertion each SERIAL_CLK_COUNT cycles */
    always@( posedge clock ) begin
        if( ~locked ) begin
            clk_counter <= 0;
        end
        else begin
            clk_counter <= clk_counter + 1;
        
            if( clk_counter == SERIAL_CLK_COUNT ) begin
                clk_counter <= 0;
                if( serial_ready )
                    serial_send <= 1'b1;
            end
            else begin
                serial_send <= 1'b0;
            end
            
        end
    end
    
    /* Serial data setting */
    always@( adc_data_out_ch1 ) begin
        serial_data_l = adc_data_out_ch1[7:0 ];
        serial_data_h = adc_data_out_ch1[15:8];    
    end
    
    assign  syzygy_p2c_clk_n    =   1'b0;
    assign  adc_test_mode       =   1'b0;
    assign  adc_data_in[ 0  ]   =   syzygy_s_24;
    assign  adc_data_in[ 1  ]   =   syzygy_s_22;
    assign  adc_data_in[ 2  ]   =   syzygy_d_n_4;
    assign  adc_data_in[ 3  ]   =   syzygy_d_p_6;
    assign  adc_data_in[ 4  ]   =   syzygy_d_n_6;
    assign  adc_data_in[ 5  ]   =   syzygy_s_16;
    assign  adc_data_in[ 6  ]   =   syzygy_s_18;
    assign  adc_data_in[ 7  ]   =   syzygy_s_20;
    assign  adc_data_in[ 8  ]   =   syzygy_s_17;
    assign  adc_data_in[ 9  ]   =   syzygy_d_p_4;
    assign  adc_data_in[ 10 ]   =   syzygy_s_19;
    assign  adc_data_in[ 11 ]   =   syzygy_s_21;
    assign  adc_data_in[ 12 ]   =   syzygy_s_23;
    assign  adc_data_in[ 13 ]   =   syzygy_s_25;
    
    /* ###################################### */
    clk_wiz_0
    u_clk_wiz_0
    (
        .clk_in1                (i_clock),
        .reset                  (i_reset),
        .clk_out1               (clock),            /* sys clock: 100MHz    */
        .clk_out2               (adc_clock),        /* adc clock: 400MHz    */
        .locked                 (locked)
    );
    /* ###################################### */
    serial #
    (
        .SERIAL_DATA_SIZE       (SERIAL_DATA_SIZE)
    )
    u_serial
    (
        .i_clock                (clock),
        .i_reset                (~locked),
        .i_send                 (serial_send),
        .i_data_h               (serial_data_h),
        .i_data_l               (serial_data_l),
        .o_ready                (serial_ready),
        .o_tx                   (o_tx)
    );
    /* ###################################### */
    ZmodADC1410_Controller_0
    u_ZmodADC1410_Controller_0
    (
        .SysClk             (clock),                //  IN STD_LOGIC;
        .ADC_InClk          (adc_clock),            //  IN STD_LOGIC;
        .sRst_n             (locked),               //  IN STD_LOGIC;
        .sInitDone_n        (adc_init_done),        //  OUT STD_LOGIC;
        .FIFO_EMPTY_CHA     (adc_fifo_empty_ch1),   //  OUT STD_LOGIC;
        .FIFO_EMPTY_CHB     (adc_fifo_empty_ch2),   //  OUT STD_LOGIC;
        .sCh1Out            (adc_data_out_ch1),     //  OUT STD_LOGIC_VECTOR(15 DOWNTO 0);
        .sCh2Out            (adc_data_out_ch2),     //  OUT STD_LOGIC_VECTOR(15 DOWNTO 0);
        .sTestMode          (adc_test_mode),        //  IN STD_LOGIC;
        .adcClkIn_p         (syzygy_c2p_clk_p),     //  OUT STD_LOGIC;
        .adcClkIn_n         (syzygy_c2p_clk_n),     //  OUT STD_LOGIC;
        .adcSync            (syzygy_s_27),          //  OUT STD_LOGIC;
        .DcoClk             (syzygy_p2c_clk_p),     //  IN STD_LOGIC;
        .dADC_Data          (adc_data_in),          //  IN STD_LOGIC_VECTOR(13 DOWNTO 0);
        .sADC_SDIO          (syzygy_d_p_2),         //  INOUT STD_LOGIC;
        .sADC_CS            (syzygy_s_26),          //  OUT STD_LOGIC;
        .sADC_Sclk          (syzygy_d_n_2),         //  OUT STD_LOGIC;
        .sCh1CouplingH      (syzygy_d_p_0),         //  OUT STD_LOGIC;
        .sCh1CouplingL      (syzygy_d_n_0),         //  OUT STD_LOGIC;
        .sCh2CouplingH      (syzygy_d_p_1),         //  OUT STD_LOGIC;
        .sCh2CouplingL      (syzygy_d_n_1),         //  OUT STD_LOGIC;
        .sCh1GainH          (syzygy_d_p_5),         //  OUT STD_LOGIC;
        .sCh1GainL          (syzygy_d_n_5),         //  OUT STD_LOGIC;
        .sCh2GainH          (syzygy_d_p_3),         //  OUT STD_LOGIC;
        .sCh2GainL          (syzygy_d_n_3),         //  OUT STD_LOGIC;
        .sRelayComH         (syzygy_d_p_7),         //  OUT STD_LOGIC;
        .sRelayComL         (syzygy_d_n_7)          //  OUT STD_LOGIC
    );
    /* ###################################### */

endmodule

The "trash data" that I'm getting comes from adc_data_out_ch1 (I'm not using CH2), and by "trash data" I mean nonsense values, for example, very unstable output values when I have a constant analog input (even with 0V). 

 

UPDATE

After researching, I realized the lack of some timing constraints that can be seen in this repository (from line 66 to 72). However, one of them is giving me problems. 

When I run

create_generated_clock -name syzygy_c2p_clk_p -source [get_pins top/ZmodADC1410_Controller_0/U0/InstADC_ClkODDR/C] -divide_by 1 [get_ports syzygy_c2p_clk_p]

I get

[Vivado 12-508] No pins matched 'top/u_ZmodADC1410_Controller_0/U0/InstADC_ClkODDR/C'.


My top level design is "top" and my Zmod ADC controller instance is "u_ZmodADC1410_Controller_0" (the first one instantiates directly the second one).
Paths I've tried (following this):  

-source [get_pins top/u_ZmodADC1410_Controller_0/U0/InstADC_ClkODDR/C]
-source [get_pins -filter {name=~ *InstADC_ClkODDR/C}] 
-source [get_pins -hier InstADC_ClkODDR/C]

All of them, with the same error message.

If I dig into the controller vhd files, I can see that 'C' pin in its expected location (that is, in 'InstADC_ClkODDR') and in the synthesized schematic.

Any ideas? I'm pretty new in this timing-related constraints.

 

Eclypse-Z7-Constraint.xdc

Edited by Tomas Sarquis
update
Link to comment
Share on other sites

10 answers to this question

Recommended Posts

  • 1

Hey Julii,

Are you using Eclypse and Zmods or some other Zynq + ADC setup? The rest of my comments below assume Eclypse along with the IP cores provided for it that handle the ADC interface.

If you're looking for the ADC configuration portion of this, the low-level low-pass filter demo might be helpful: https://digilent.com/reference/programmable-logic/eclypse-z7/demos/low-level-low-pass-filter. The ZmodScopeController IP used in the demo is specifically targetted to the Eclypse and the Zmod products that support it, but its architecture might be relevant for implementing a similar controller for another ADC and board - depending on the vendor of your parts, you may find similar controllers and examples provided by them.

For designing the HLS module, Digilent doesn't have much if any material for it on the web. I'd recommend starting out by finding a passthrough example that you can work from and modifying it to suit your needs. This might be suitable (and the rest of UG1399 is also relevant): https://docs.xilinx.com/r/2023.1-English/ug1399-vitis-hls/AXI4-Stream-Interfaces-without-Side-Channels.

How data is packed in the source AXI stream interface is also extremely relevant. For example, for the ZmodScopeController, the data format is defined in the IP Top Level Port Description table of the user guide on page 21 (particularly cDataAxisTdata), and in the ADC Calibration section, pages 7-9: https://github.com/Digilent/vivado-library/blob/master/ip/Zmods/ZmodScopeController/docs/ZmodScopeController.pdf.

Thanks,

Arthur

Link to comment
Share on other sites

  • 0

I appreciate that throwing code over the wall and hoping that someone spots the (hopefully) one or two defects that will fix the problem is an easy and quick solution... it's a common approach around this forum.

Perhaps there is a more productive way.

First, since you want to do an all HDL design your sources are missing at least one key element... a Verilog testbench. Part of the design process is verification, and at a minimum behavioral simulation is one part of verification. Simulation will help figure out what's going on with your design. To my way of thinking, if someone claims to have a design and there are no testbenches among the sources, then my first thought is to suggest that they still have some work to do before trying to implement an unfinished design in hardware.

If someone came to me in person with such a question as yours the first question, even before "where's the testbench?", would be "start by providing a brief description of how your design is supposed to work". Perhaps, you could start with that. I can't tell you how many times just asking that question has produced answers to questions; as having to put into words a representation of an amalgam of concepts often reveals bad assumptions and missing elements. Having someone stop, mid sentence as the lightbulb turns on, is most satisfying for everyone.

Link to comment
Share on other sites

  • 0

Hello @zygot, thank you for your reply.

I got your point about "posting the code and hoping someone to fix it", but I did it because I may have some bad connections and this is one way of show you how the connections are made (maybe a diagram would be better?).

The purpose of the design is quite simple: digitalize some AC signals and then process them.

I didn't think about the testbench thing before now... but, ¿how could I simulate the IP behaviour without synthetizing and using the "real" ADC? 
I should simulate all the internal signals and responses? That sounds pretty complex...

Link to comment
Share on other sites

  • 0
17 hours ago, Tomas Sarquis said:

The purpose of the design is quite simple: digitalize some AC signals and then process them.

Perhaps that's the purpose but it isn't a description of how the design is supposed to work. No one can help you debug your purpose but perhaps they might have some insight into how you can debug the design. So... again, how's the design supposed to work as you envision it?

 

17 hours ago, Tomas Sarquis said:

how could I simulate the IP behaviour without synthetizing and using the "real" ADC? 
I should simulate all the internal signals and responses? That sounds pretty complex...

There are a lot of ways to replace the ADC devices in your simulation testbench. The easiest would be to have a process that spits out incrementing data. You don't have to worry about output code formats... just refer to the ADC datasheet and make sure that the data is transferred accordingly. One of the nice things about Verilog for simulation is that you can use parts of Verilog that aren't supported by synthesis. You can read a file with ADC sample words and feed that into your testbench. Trying to see "actual real-world ADC sample data" in a simulation probably not a good choice for a number of reasons.

Really, the more important thing about the simulation is getting confirmation that, on a behavioral or RTL level at least, the whole design is doing what you think that it's doing. Simulating ADC formats isn't necessarily the first thing that you want to do.

I realize that there isn't a lot of texts available on good simulation techniques but you need to develop your verification chops in step with your design flow skills.

One problem with designing testbench code is that you are adding another thing to debug. It's analogous to unit testing software modules. The point is that, as your designs get more complex and hard to follow, your verification skills need to get more sophisticated and crafty as well. Your problem isn't that your design isn't working.. it's that your debugging skills aren't up to the task of figuring out how to find the cause of it's not working.

 

Edited by zygot
Link to comment
Share on other sites

  • 0
On 5/1/2021 at 12:28 AM, tsarquis said:

create_generated_clock -name syzygy_c2p_clk_p -source [get_pins top/ZmodADC1410_Controller_0/U0/InstADC_ClkODDR/C] -divide_by 1 [get_ports syzygy_c2p_clk_p]

 

I get similar warning. How did you solve that.

Link to comment
Share on other sites

  • 0

Hey everyone,

I'm currently working on a project that involves an Analog-to-Digital Converter (ADC) and a High-Level Synthesis (HLS) core in an FPGA environment. My goal is to streamline the data processing pipeline by having the ADC stream its data directly to the HLS core, bypassing the usual step of writing the data to memory through the S2MM (Stream to Memory-Mapped) interface.

From what I understand, using the S2MM interface to first write ADC data to memory before it's processed by an HLS core is a common approach. However, for my application, this adds unnecessary latency and complexity, especially since the data doesn't need to be stored but rather processed in real-time by the HLS core.

Does anyone have experience or insights on configuring the ADC to stream data directly to an HLS core?

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...