Dump external ADC data (SPI, USB, DMA, pulse generation)

Status
Not open for further replies.

RasmusB

New member
Hello!

I am building an FPGA-based data measurement system which uses an AD7980 AD converter. I have just received the first prototype of the AD modules, but the FPGA part is not done yet.

I want to test the ADC at Msa/s with as low jitter as possbile, and I was hoping that I could use an Teensy 3.0 for this. There are three things I need to do this:

  1. Generate a trigger pulse (<290ns @ 1MHz) to start conversion
  2. Receive the "data ready" signal from the ACD and read out the 16 bits of data VERY quickly (see p. 18 in the AD7980 data sheet)
  3. Sent the data to the PC via USB

Based on my own quick tests so far, it seems that (1) is easily done with analogWrite.
I still haven't figured out how to get 16-bit SPI transfers, but from benchmarks I have seen it seems that 16 Mbit/s is feasible. USB transfer speed could also be sufficient.

Now, the question is if it is even remotely possible to do all this at once? One issue is creating an interrupt which can react to the "data ready" signal quickly enough (~88 ns!) and start to shift the data out @ ~84 MHz. And after that, I need to send the data over USB. I was hoping to see if anyone had written an example of transferring data from SPI->USB with DMA... but the issue of getting the SPI transfer started quickly enough still remains.

Thank you for reading!
 
Last edited:
Teensy 3.0 has 12 Mbit/sec USB, which has a maximum theoretical limit of 1216 kbytes/sec, not including the data-dependent overhead from USB's bit stuffing algorithm.

In practice, the maximum speed usually ends up being about 1 Mbyte/sec.

Teensy 3.0 is capable of moving 1 Mbyte/sec pretty easily, even with some memory-to-memory copying involved. It might be possible to sustain 2 Mbyte/sec speed? The SPI maximum speed is 24 Mbit/sec, which ought to be plenty.

I have not personally used the SPI port with DMA yet, but everything I'd read looks like this ought to be possible. The SPI even has a small FIFO, so it can tolerate a fair amount of latency from the DMA response, so I believe you have a pretty good shot at making this work. You might need to use a pair of DMA channels with the major loop linking feature. As each channel completes its transfer, the linked channel automatically begins. That why, you can get an interrupt when each buffer fills up, but the next buffer begins filling automatically using the other DMA channel, even if your interrupt code has some latency. All you have to do is assign a new buffer to the stopped DMA channel before the active channel completes. Well, and of course do something with the data.

Teensy 3.0 is probably fast enough that you could just set a flag indicating a buffer of data is ready, and of course store a pointer to which buffer, and then poll that flag in the main program and use Serial.write() with the pointer. That's obviously not the most efficient way, since Serial.write() will allocate a buffer for the USB DMA and memcpy the data. But the ARM core is pretty fast, so at 1 MByte/sec, I believe it can probably handle this pretty easily.

A more efficient approach, which probably isn't worth all the trouble, would involve hacking the USB code somewhat. Then instead of allocating your own buffers, you could call usb_malloc() to get one of USB stack's 64 byte buffers. You could have your SPI's DMA put the data directly into that 64 byte buffer, and probably have you DMA completion interrupts manage a small ring buffer of pointers to those USB buffers. Then in your main program, you could pass the DMA buffer directly to the USB code (using whatever function you add to accept a pre-filled USB buffer... but if you look at the code in usb_serial.c, you'll see this should be relatively easy). This approach should give you the most efficient code. It's probably overkill.

But no matter what you do, it's absolutely impossible to move data 2 MByte/sec over 12 Mbyte/sec USB. In practice, 1 Mbyte/sec is about the fastest that can be achieved. A realistic goal might be to run at 800 kbytes/sec, which is unfortunately only 40% of the AD7980's maximum speed.
 
Thank you for a very thorough answer Paul, I really appreciate it!

I did not think that the USB speed would be the limiting factor; I must have misread 12Mbit as 12Mbyte somewhere. :eek:

I really do need to run the ADC at full speed for my tests though. There are of course USB<->SPI adapters there, but they would still need to be synchronized to a jitter-free trigger pulse. I have seen some adapters that have a few GPIO pins that can be controlled by user software, but I believe that the USB + software latency would make it impossible to toggle those at 1MHz without jitter.

Can you think of any alternate interfaces that could get the job done? (Let's assume I can get the pulse generation + 2Mbyte SPI working...). Some kind of memory mapped device, network interface, external USB device etc...?
 
I would probably use the Cypress FX2 chip and a FPGA or CLPD, clocked by a low-jitter crystal oscillator, for a serial to parallel conversion and "glue logic" between the AD7980 SPI and FX2's 16 bit bus.

If this is an academic project, where a hard deadline imposed by the academic calendar overrides all other factors, I would first try to get my advisor to agree to a 400 ksample/sec milestone and some sort of agreement that achieving the full speed might require more time than is available. An "A for effort" is usually much easier with working results at a good milestone!
 
Last edited:
You should be able to do this without interrupts (using DMA), by just using fixed timings. If, you look at the K20 datasheet, you can set up various delays for selecting/deselecting CS and use the 'Without Busy Indicator' mode of the AD7980.

Depending on your captured data, you may be able to use compression. E.g. use the difference between samples and Huffman-encode that. I think, the Teensy should be fast enough. (If you don't absolutely need the low noisy bits and can throw them away, you get much better compression.)
 
Last edited:
I would probably use the Cypress FX2 chip and a FPGA or CLPD, clocked by a low-jitter crystal oscillator, for a serial to parallel conversion and "glue logic" between the AD7980 SPI and FX2's 16 bit bus.

If this is an academic project, where a hard deadline imposed by the academic calendar overrides all other factors, I would first try to get my advisor to agree to a 400 ksample/sec milestone and some sort of agreement that achieving the full speed might require more time than is available. An "A for effort" is usually much easier with working results at a good milestone!

Thank you Paul. Unfortunately for me it's a work project :) I'll look into the Cypress chip - I should be able to add one to the FPGA hardware; I'll just have to give up on the idea to be able to verify my AD module before the FPGA hardware is completed. Not a biggie since it is a modular design after all. Thank you for your input.

Thank you tni for your suggestion as well, but I really need the full speed and resolution - it is the noise levels at full speed that are my main concern.
 
If you want noise levels it might be possible to simply calculate the mean and variance for a small number, say 8,
of samples, and then send this data over usb. These calculations can be done very efficiently on the Teensy 3,
using the Cortex-M4 DSP instructions.

Regards,
Magnus
 
Status
Not open for further replies.
Back
Top