Fast ADC interface with FPGA - Page 1
Forum section: electronics / general

author: knight
author_rank: Regular Contributor
timestamp: August 16, 2022, 03:30:34 pm
content: I have to interface a fast ADC with an FPGA and then do the data processing. The FPGA used will be Zynq Ultrascale+ RFSoC ZU29DR. I have been given the information that ADC_clk = 4x FPGA_clk. ADC output data is 12/16-bits. The data processing will be multiply-accumulate over a number of samples.Code:I = I + adc_data * cos;Q = Q + adc_data * sin;There is a data sample output at every clock of ADC. So, it will be something like:Code:I = I + adc_data * cos;Q = Q + adc_data * sin;I = I + adc_data * cos;Q = Q + adc_data * sin;And so on.I have a DDS CORDIC-LUT module that can be instantiated to generate 4 sin, cos values parallelly based on the phase word. What I'm not sure about is how to multiply with adc_data as adc generates faster than what the FPGA can accept.I have come up with the below timing diagram and architecture. Any input will be appreciated.

author: hamster_nz
author_rank: Super Contributor
timestamp: August 16, 2022, 09:45:13 pm
content: That timing diagram looks a little odd to me - there is eight data values per FPGA clock cycle, not four.Not sure what the question is (if any), but you will need to use SERDES to separate out the four samples.See attached diagram for what you need to construct for each bit (the PLL gets shared by all bits).You will need to get very friendly with:- the simulator (to check it works before trying in H/W)- The clocking resources on your FPGA - you have to write your design to map onto the FPGA's dedicated structures. To not do so will result in failure.- Documentation for the SERDES block on your FPGA - they are simple to use, but have so many options, and it can be tricky to reset them correctly.The other option is to clock the edge of the design twice as fast, and then use the much simpler DDR registers to capture the data.This might be of use (even though Ultrascale I/O hardware is different):https://github.com/hamsternz/Artix-7-HDMI-processing/blob/master/src/deserialiser_1_to_10.vhd

author: hamster_nz
author_rank: Super Contributor
timestamp: August 16, 2022, 09:53:19 pm
content: Oh, and if latency isn't an issue you can use the sample input as the initial CORDIC vector, so when you calculate SIN & COS you will actually get X*SIN(phase)*CORDIC_GAIN and X*COS(phase)*CORDIC_GAIN, removing the need for any multipliers.

author: knight
author_rank: Regular Contributor
timestamp: August 17, 2022, 01:22:20 am
content: Quote from: hamster_nz on August 16, 2022, 09:45:13 pm

author: knight
author_rank: Regular Contributor
timestamp: August 17, 2022, 01:23:33 am
content: Quote from: hamster_nz on August 16, 2022, 09:53:19 pmSounds good

author: hamster_nz
author_rank: Super Contributor
timestamp: August 17, 2022, 02:10:22 am
content: Quote from: knight on August 17, 2022, 01:22:20 amYour FPGA internal signals (sine and cosines) should only change on one of the FPGA clock edges (either on the rising or falling, but not both). Have a look at the attached trace and see if that is what you want to happen at the edge of your design (before the multipliers).What is preventing you from using SERDES? The FPGA supports SERDES, and without it there is no sensible synthesizable way to input or output data at 4x the FPGA logic's clock rate.Are they other restrictions that you are not telling us about? It's really frustrating to put the time in to provide a sensible answer, only to be told "sorry - wrong answer - I'm not allowed to do that, try again".

author: knight
author_rank: Regular Contributor
timestamp: August 17, 2022, 02:45:56 am
content: Quote from: hamster_nz on August 17, 2022, 02:10:22 am

author: hamster_nz
author_rank: Super Contributor
timestamp: August 17, 2022, 03:15:29 am
content: Oh, yes, that's different!I would consider using a four-sample-long shift register on the high speed side, and then latch it in the slow clock domain. As long as the fast and slow clocks have a known phase relationship (i.e. sourced from the same PLL) you shouldn't have any timing problems with that, as the tools will be able to verify the timing works - so no FIFO or shared memory block is needed (or even synchronizers!).For a 12-bit sample you will need a 48-bit shift register.For the fast clock domain it is nothing but:Code:if rising_edge(fast_clk) thenshift_reg <= shift_reg(35 downto 0) & new_sampleend if;and for the slow domainCode:if rising_edge(slow_clk) thensample3 <= shift_reg(47 downto 36); -- Oldest samplesample2 <= shift_reg(35 downto 24);sample1 <= shift_reg(23 downto 12);sample0 <= shift_reg(11 downto 0); -- Most recent sampleend if;Then you can do your math on the four sample values.

author: Someone
author_rank: Super Contributor
timestamp: August 17, 2022, 03:17:34 am
content: Quote from: hamster_nz on August 17, 2022, 02:10:22 amThis is already a split thread:https://www.eevblog.com/forum/fpga/how-to-use-fifo-to-process-fast-adc-data/But the data arriving interleaved on the ADC clock (unsure if word or bit interleaved from description so far) obviously has a clock the FPGA is handling somehow....

author: knight
author_rank: Regular Contributor
timestamp: August 17, 2022, 04:33:30 am
content: Quote from: hamster_nz on August 17, 2022, 03:15:29 amThanks bro, I will try to code it.

author: radiolistener
author_rank: Super Contributor
timestamp: August 17, 2022, 07:41:29 am
content: since your ADC works at x4 clock frequency, you can use ADC ready signal as a clock for processing ADC data lines at x4 clock rate. Then transfer data into slow FPGA clock domain...

author: knight
author_rank: Regular Contributor
timestamp: August 17, 2022, 01:05:52 pm
content: Quote from: hamster_nz on August 17, 2022, 03:15:29 am

author: hamster_nz
author_rank: Super Contributor
timestamp: August 17, 2022, 09:13:03 pm
content: & in VHDL is the concatenation operator, & in Verilog is binary AND operator.I think you want something like:Code:shift_reg <= { shift_reg[35:0], adc_data_in };

author: knight
author_rank: Regular Contributor
timestamp: August 18, 2022, 03:22:11 am
content: Quote from: hamster_nz on August 17, 2022, 09:13:03 pmThanks! It's correct. VHDL is a weird language to use "and" as concatenation operator.

author: SiliconWizard
author_rank: Super Contributor
timestamp: August 18, 2022, 04:50:24 pm
content: Quote from: knight on August 18, 2022, 03:22:11 amVHDL is based on Ada which uses "and" as a "and" operator. Now which is weird?

author: knight
author_rank: Regular Contributor
timestamp: August 28, 2022, 01:58:58 pm
content: I tried to code the proposed architecture in Verilog HDL and got some results, as attached in the picture below. However, there seems to be some minor issue which I cannot find, the sample count doesn't go upto 65536 which it is supposed to. It stops at 65528 and then goes to 0. I'm including the Vivado project rar file so that it is easier for people to run it. The issue is somewhere in the "raw_sp.v" file. Any inputs will be highly appreciated. Thanks.

author: knight
author_rank: Regular Contributor
timestamp: September 13, 2022, 01:05:10 am
content: Quote from: hamster_nz on August 17, 2022, 03:15:29 amThe above logic seems to be doing clock domain crossing. Wouldn't it need some kind of FIFO to work?

author: ejeffrey
author_rank: Super Contributor
timestamp: September 13, 2022, 04:46:11 am
content: Quote from: knight on September 13, 2022, 01:05:10 amIf fast_clk and slow_clk are synchronous with a defined and fixed phase (say one generated from a multiple output PLL) then they are effectively the same clock domain and the timing analyzer will be able to determine if the setup/hold limits are violated.This is somewhat unusual and someone reading the code might well think that there is a true clock-domain crossing done incorrectly, so it is worth clearly documenting your expectations and of course make sure you set up the timing constraints properly.

author: knight
author_rank: Regular Contributor
timestamp: September 13, 2022, 08:51:16 am
content: QuoteThank you very much for your reply. I'm using Zynq Ultrascale+ RFSoC. How does one find out if clocks are synchronous/generated from same PLL?