h1

ADC In An FPGA, Part 2

June 5, 2011

This is part 2 of “ADC In an FPGA”.  If you have not read part 1, here it is.

Part 1 covered an overview of the ADC design.  Part 2 is going to cover a small part of it, but in more detail.  Specifically, the math around the RC filter model and how to optimize it to almost nothing.

The RC filter model is basically a chunk of logic inside the FPGA that attempts to predict what the external RC filter is actually doing, without directly measuring it.  The math behind this is conceptually easy, so here’s the formula.

New_Voltage = Current_Voltage + (RC_Input_Voltage – Current_Voltage) * (1-exp(-T/RC))

Where:

T = Clock period, in seconds
R = Resistor, in ohms
C = Capacitor, in  Farads
Current_Voltage = The current output voltage of the RC filter
RC_Input_Voltage = The output of the FPGA, going into the RC filter
New_Voltage = The output voltage of the RC filter after T seconds
exp() = The exponential function.

At first glance, this looks like it would be hard to implement in an FPGA, but rest assured that it isn’t.

The first optimization that we’re going to do is change “voltage” to be a value from 0.0 to 1.0.  Basically, a percentage of the power rail voltage.  So if our I/O Voltage is 3.3v then a value of 1.0 would represent a voltage of 3.3, and a value of 0.5 would represent 1.65 volts.  This is super easy to do, all you do is change the definitions of the variables in the above formula.  The new formula is:

New_Value = Current_Value + (RC_Input_Value – Current_Value) * (1-exp(-T/RC))

Where all of the “values” have a range of 0.0 to 1.0.  And since the FPGA can only output ‘0’ or ‘1’, the acceptable values for RC_Input_Value can only be 0.0 or 1.0 and nothing in between.

Of course in the FPGA we would not be doing the math for this as floating point (“real” numbers in VHDL).  That would be silly.  Instead we’ll use the standard fixed point notation where (using a 4-bit vector for example) “0000” would be a value of 0.0.  “0111” would be, essentially 1.0. “1000” would be -1.0.  And “0010” would be +0.5.

The next thing to optimize is the  ” * (1-exp(-T/RC))” part.  It is important to note that since T, R, and C do not change once the design is done that this expression simplifies down to multiplication by a constant.  In an FPGA, multiplication by an arbitrary constant is easy but can take up a lot of logic.  Either it will use up some integer multiplier inside the FPGA, or end up as some crazy adder tree.  Sometimes that’s appropriate, but we can do better.

So we don’t want to multiply by an arbitrary constant.  But if our constant had only a single bit set in it, then our multiplication turns into a simple bit-shift, and a bit-shift in an FPGA takes no logic at all!  We do have control over what R and C are, and to a lesser extent T.  If we choose our R and C carefully then we can find a constant with only 1 bit set– and our multiplication becomes dirt simple.

Finding the appropriate R and C is not simple.  If you did it by hand it would take lots of tedious calculations.  Fortunately, computers are good at that sort of thing.  I wrote a program that, when given T, will calculate all R’s and C’s that have a nice constant value associated with them.

It works like this:  Let’s assume that R and C are 3.16K and 200 pF, and our T is 10 nS.  From that we can calculate that our constant is 0.015698261559.    When we convert it to our fixed point representation, we get 0x0202668F, assuming 32 bit signed numbers.  If we then chop off all but the first ‘1’ bit, we get 0x02000000.  Now, 0x02000000 looks a lot different than 0x0202668F, but if you convert it back into normal floating point (0.015625000000) you’ll see that it is about 0.47% off from the original number.  In the grand scheme of things 0.47% is not a lot considering the resistor has a tolerance of 1% and the cap is much worse.

My program does this for all combinations of R and C, and spits out the combinations that are within 1% of the ideal value.   Click on adc_constants to get an Excel file with the most popular clock frequencies already calculated.  Inside the file are multiple sheets, one for each clock frequency.   Each sheet has the following columns:

R — The resistor value
C — The capacitor value
Const(ideal) — The ideal value for our (1-exp(-T/RC)) constant, without rounding and stuff.
Const(int) — The 32-bit integer representation of our constant, in hex, and after all the bit mangling.
Const(float) — The floating point version of Const(int), after bit mangling.
Const Err– The error between Const(float) and Const(ideal).
n_bits — The number of ‘1’ bits in Const(int)
rc — The RC time constant for R and C

The easiest way to use this file is to go to the sheet with your clock frequency, and pick out an RC combination that is close to what you want.  I strongly suggest using the Excel sort data function to put the data into an order that makes life easier.   By default, it is sorted by R and then C.  But sorting by rc and then by Const Err could be useful too.  Either way, pick out an RC and go to town!

I should also note that the table does not include resistors smaller than 3K ohms.  This is intentional.  If the resistor is any smaller then the load on the FPGA output pin is too high to allow the output to switch rail to rail.  Rail to rail outputs is essential to keeping everyone sane.

For some reason, this blog software doesn’t allow me to post Zip files, EXE’s, or normal text files.  Once I figure out how to do this I can post the source code and EXE for the software that generates these tables.  With that software, you can calculate RC’s for odd-ball clock frequencies or constants with more than just 1 bit set in it.

Once the dust settles, our original formula has turned into a simple bit shift and addition.   Ya can’t get much simpler than that!

Next ADC post will cover more of the low pass filter, and how to optimize that one to death as well.

Advertisements

4 comments

  1. […]  Part 2 of this series is up.  Read it here, when you’re done with Part 1, of […]


  2. I’m an embedded/real-time programmer just starting down the FPGA learning curve.

    I have a no-frills breakout board for a Xilinx Spartan 3E500K-4 PQ208, and for my first project I want to do something “interesting” that will require minimal external components or interfaces (beyond JTAG).

    A digital audio effects system (guitar, voice, etc.) is my choice. For output, I figure my DAC will use digital output pins and a resistor ladder. My search for an all-digital audio-rate ADC led me to your blog.

    The neat thing about doing an audio effects generator is that the clock rates are fairly low and ADC only has to be ‘good enough’. And it may be possible to implement some effects within the ADC itself.

    I particularly like how your blog posts discuss not only the basic theory, but also address optimizations and other implementation considerations.

    Do you have any HDL code you’d care to share?


  3. I am a graduate student working on implementing soft ADCs utilizing just this approach for an advanced digital course. I realize this article is a few years old now, but do you plan on posting part 3?


  4. Hi, I am very interested in making a Drum kit midi interface in a Lattice ICE40 based FPGA using this technique. Can you please send me any HDL code (prefer Verilog, but anything would be great).

    Thanks.



Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: