0

I am trying to convert a value from linear notation to decibel notation on an FPGA.

While the equation x_dB=10*log(x_lin) is certainly well known, but I have been unable to implement it in VHDL.

I have found some identities (with arctanh being the most common) which MAY be of use at http://en.wikipedia.org/wiki/Inverse_hyperbolic_function#Logarithmic_representation

Since inverse hyperbolic functions are available through the COordinate Rotation DIgital Computer (CORDIC), this is seems easy enough--except the CORDIC has two inputs (X & Y) as opposed to the single input required by a logarithm, and for that matter the hyperbolic arc tangent function! How do I figure out what to use for the two input values (x & y) given that I'm really trying to do is a log?

jmink3
  • 1
  • 1
  • 2
  • If you only have a small number of dB values, you could use a lookup table. – Andrew Morton Aug 05 '14 at 19:05
  • What is the bit size of x_lin, what is the required accuracy of the result (e.g. is +/- 1% accuracy acceptable), and what is the required conversion speed (number of cycles available for each conversion)? – Morten Zilmer Aug 05 '14 at 20:10
  • 2
    In keeping with the two previous comments - [Quick and Easy Binary to dB Conversion](http://www.people.vcu.edu/~jhtucker/s09-egre631/LHO%2006b%20IEEESECON07%20Quick%20and%20Easy%20Binary%20to%20dB%20Conversion.pdf "Quick and Easy Binary to dB Conversion") - "The algorithm could also easily be coded in VHDL and synthesized for implementation in an FPGA." And if your question had been specific enough it could have been an answer. –  Aug 05 '14 at 22:59
  • There are dozens of methods to compute (or approximate) arbitrary functions. Much depends on the required precision, requirement of floating point vs integers, timing constraints, logic constraints etc. Please elaborate on what you need exactly. – Philippe Aug 06 '14 at 08:34
  • The LUT currently the backup plan. We are shying away from this approach as there is a large dynamic range of values (even in dB) and 1% error is not acceptable, it needs to be more like 0.01% (or even less) meaning we would have a rather large table eating up precious space. Furthermore, there is not a huge constraint with system latency. The FPGA clock is 100MHz and the system requirement is on the order of milliseconds, so I'm not overly concerned with counting cycles. @Morten we are using 16 bit fixed point but future-proofing for 32 bit as well. – jmink3 Aug 06 '14 at 17:05
  • So I guess you haven't got space to [emulate a 6502](https://www.google.com/?gws_rd=ssl#q=6502+vhdl+code) and use [Steve Wozniak's code for log10](http://www.6502.org/source/floats/wozfp1.txt)? – Andrew Morton Aug 06 '14 at 17:19
  • @AndrewMorton sadly that seems a bit excessive...But it might get a good laugh at one of the team meetings! – jmink3 Aug 06 '14 at 17:46
  • @Philippe I've done some searching on that topic, and most of the posts I came across seemed to agree that the inverse hyperbolic tangent was the best approach. If you know of others, I'd be happy to consider those too! – jmink3 Aug 06 '14 at 17:48
  • @jmink3 You probably refer to general methods for computations on a floating point ISP. On the extremely simple side of the spectrum, you could go with lookup tables and approximations using piecewise polynomial approximation. You haven't shared any requirements about input and output datatypes or timing constraints, which are *crucial* in this question. – Philippe Aug 07 '14 at 14:07
  • @Philippe not much room for explanations here, but to explicitly state...the strict requirement is to maintain a 100MHz clock (10ns) as that is our data rate. We are working with 16 bit numbers now, but I'm future-proofing with 32 bits where possible. Note: latency is not a huge concern as our results are only relevant at the ms scale, giving us millions of clock cycles. So rather than focusing on how many cycles a design takes, I'm much more concerned with how big the footprint is on the board (in terms of what else I can put on the board). – jmink3 Aug 07 '14 at 15:28

1 Answers1

1

Your goal is to solve for the natural log using the identity:

ln(w) = 2 * atanh((w-1) / (w+1))

You have to set up a hyberbolic version of CORDIC in vectoring mode (y --> 0) that evaluates atanh(y/x). That is accomplished similarly to normal CORDIC by setting y to w-1 and x to w+1 and initial angle z to 0. After iterating, the result will be in z after applying a left shift for multiplication by 2. Take note that the hyberbolic extension to CORDIC requires repeating certain iterations (4, 13, 40, ...) for it to converge.

Once you have ln(w) you need to multiply by the constant 10 / ln(10) to get the base-10 logarithm in dB.

Ray Andraka's paper gives a good general overview of CORDIC implementations. It glosses a bit over the hyperbolic extension. These slides from Xilinx cover more of the details of the hyberbolic CORDIC.

Kevin Thibedeau
  • 3,299
  • 15
  • 26
  • Thanks for the resources! I will take a look at those and see if they help clarify (or with my luck, further amplify) the confusion. – jmink3 Aug 06 '14 at 17:12
  • "You have to set up a hyberbolic version of CORDIC in vectoring mode (y --> 0) that evaluates atanh(y/x)." <--tried y = 0, didn't work. "That is accomplished similarly to normal CORDIC by setting y to w-1 and x to w+1 and initial angle z to 0." Tried this too, despite seemly contradicting what you said above. Regardless I did not get anything resembling a natural log. And yes, I've tried switching X & Y just to make sure it wasn't a "oops, got those backwards" type thing. – jmink3 Aug 08 '14 at 15:58
  • You don't set Y = 0. The decision steps on each iteration in vectoring mode are selected such that y approaches 0 i.e. Y --> 0. You can't switch X and Y since the decision will be based off the wrong parameter. Moreover, the hyperbolic version adds or subtracts from X and Y with the same sign anyway. You have a mistake somewhere. I suggest prototyping the algorithm in a scripting language such as Python to get a working reference. – Kevin Thibedeau Aug 08 '14 at 17:22
  • yes. very confused on this end! I understand the CORDIC implementation of arctanh is iterating along a curve, from a starting vector to the x-intercept, and summing the area beneath the curve to calculate arctanh. I certainly trust that CORDIC is well tested and functioning correctly (especially on such a basic test). But I don't have the **faintest idea** how to do the comparison you recommend, as I am thoroughly confused as to how to take the w input from "find ln(w)" and generate the x & y values for the CORDIC initial vector, or vice-versa! – jmink3 Aug 08 '14 at 18:52
  • For example: to find ln(17), how do I generate a vector to integrate over from "17"? – jmink3 Aug 08 '14 at 18:55
  • 1
    2 * atanh(16/18) = 2.8332 = ln(17). You perform a hyperbolic CORDIC with Y = 16.0 and X = 18.0. The decision in each step is part of the basic algorithm. You use the sign of the Y register to determine whether to add or subtract on each iteration. On each iteration you add or subtract gradually smaller angles from the atanh table to Z until it becomes a close approximation to the result. Implement the algorithm in a scripting language using floating point then convert it to fixed point VHDL for synthesis. – Kevin Thibedeau Aug 09 '14 at 00:01
  • Ok it IS the u +/- 1 trick, where ln(u) is found by setting the inputs of arctanh to y=u+1 and x=u-1. This means that the CORDIC implementation of arctanh is extremely limited (and for me doesn't work) as a natural logarithm, since it requires the it requires a boundary condition for convergence of |y| < 0.8*|x| and since y=x+2, this only works for values of u<10. – jmink3 Aug 11 '14 at 16:59