Convert ADC output format to DAC input

Discussion in 'VHDL' started by snake368, Apr 16, 2013.

  1. snake368

    snake368 Guest

    I want to do the following operation using VHDL. The INPUT and OUTPUT are 14-bit signals and I have to divide INPUT by a constant, in this case 132, which is in decimal form.
    Here's my code:


    PROCESS (clk)
    VARIABLE d, q: INTEGER;
    BEGIN
    IF (clk'EVENT AND clk = '1') THEN
    CASE state IS
    WHEN min_132000 =>
    d := CONV_INTEGER (a);
    q := 0;
    IF (d >= 132000) THEN
    d := d - 132000;
    q := q + 1000;
    END IF;
    IF (d >= 13200) THEN
    state <= min_13200;
    ELSIF (d >= 1320) THEN
    state <= min_1320;
    ELSIF (d >= 132) THEN
    state <= min_132;
    ELSE
    state <= to_dac;
    END IF;
    WHEN min_13200 =>
    IF (d >= 13200) THEN
    d := d - 13200;
    q := q + 100;
    END IF;
    IF (d < 13200) THEN
    IF (d >= 1320) THEN
    state <= min_1320;
    ELSIF (d >= 132) THEN
    state <= min_132;
    ELSE
    state <= to_dac;
    END IF;
    END IF;
    WHEN min_1320 =>
    IF (d >= 1320) THEN
    d := d - 1320;
    q := q + 10;
    END IF;
    IF (d < 1320) THEN
    IF (d >= 132) THEN
    state <= min_132;
    ELSE
    state <= to_dac;
    END IF;
    END IF;
    WHEN min_132 =>
    IF (d >= 132) THEN
    d := d - 132;
    q := q + 1;
    END IF;
    IF (d < 132) THEN
    state <= end;
    b <= CONV_STD_LOGIC_VECTOR ((q), 14);
    END IF;
    WHEN end =>
    state <= end;
    END CASE;

    As you see it's a FSM and takes several rising edges to complete. Is there anyway to do it easier? Or maybe in a single rising edge?
     
    snake368, Apr 16, 2013
    #1
    1. Advertisements

  2. snake368

    goouse99 Guest

    Am Mittwoch, 17. April 2013 00:36:48 UTC+2 schrieb :
    Hi,
    are you going to synthesize this?
    You should consider using :
    1) the numeric.std library
    2) signed/unsigned types instead of integer

    It's quite unusual to see things done in decimal rather than binary numbers, but for your division algorithm you are saving some clock cycles this way.

    Multiplication and division will always take some clock cycles in a standard implemetation.
    There are several ways you could improve the performance of your design.
    Instead of a statemachine, you could create a pipelined design.
    There you would have the same latency, but since you can fill data into the pipeline at each clock cycle you will also get results after each clock cycle.
    The FSM solution can only produce one result every N-th clock.

    Also you might think about using some dedicated multiplier (e.g. DSP48 Macro in Xilinx devices). You can either use some IP-Generator or use a multiplier with the inverse divisor.
    So instead of A/B you calculate A*(B^-1).

    Have a nice synthesis
    Eilert
     
    goouse99, Apr 17, 2013
    #2
    1. Advertisements

  3. snake368

    snake368 Guest

    Hi,
    Thank you for your time. I am using ieee.std_logic_arith package (which I forgot to include!). I appreciate your point about using binary numbers and I will consider it.

    If I use a dedicated multiplier or an IP Core, will the result be ready in a single clock cycle?

    Also, I didn't understand your approach on the pipelined design, could you explain a little more or just post a code snippet as an example?

    Thanks in advance.
    Arash
     
    snake368, Apr 17, 2013
    #3
  4. snake368

    Andy Guest

    I would avoid using std_logic_arith. It is non-standard, non-compliant, differs between tools and vendors, and is generally a bad idea.

    If your tools support vhdl-2008, there is a new package ieee.numeric_std_unsigned, which difines unsigned arithmetic and conversions to/from integer, but is officially balloted, approved and supported by IEEE. Use to_integer() and to_slv() functions from this package.

    I disagree whole-heartedly with advice to avoid using integers in synthesizable RTL. They are accepted by every synthesis tool I know of, and have several advantages, not the least of which is a huge increase in simulation performance. All HW is implemented in binary, no matter how you describe it (in decimal, octal, hex, etc). If you want to describe binary using integers, simply base your literals: 2#1011# = 16#b# = 11.

    Andy
     
    Andy, Apr 17, 2013
    #4
  5. snake368

    rickman Guest

    You have the right idea, sort of. I see two problems. One is that you
    are thinking in decimal while your data is really binary. The other is
    that you don't fully understand how VHDL works in processes.

    Let's deal with the second problem first. Looking at the case for WHEN
    min_132000 => you will see that every time you enter this section the
    variables d and q are initialized and yet you only subtract 132000 once
    before leaving. So nothing outside of the process is changed and it
    will repeat these same operations on every clock edge without updating
    either a or b.

    Rather than coding this using decimal notation why not think in terms of
    binary? Remember your long division? Try doing that on paper using
    binary numbers. Say a = 133,000. Then the long division will be...
    1000000011...
    ________________________||||||||||
    0010 0000 0011 1010 0000)0010 0000 0111 1000 1000|||||||||
    - 0010 0000 0011 1010 0000|||||||||
    ------------------------|||||||||
    0000 0000 0111 1101 0000||||||||
    0000 0000 0000 0000 0000||||||||
    ------------------------||||||||
    0000 0000 1111 1010 0000|||||||
    0000 0000 0000 0000 0000|||||||
    ------------------------|||||||
    0000 0001 1111 0100 0000||||||
    0000 0000 0000 0000 0000||||||
    ------------------------||||||
    0000 0011 1110 1000 0000|||||
    0000 0000 0000 0000 0000|||||
    ------------------------|||||
    0000 0111 1101 0000 0000||||
    0000 0000 0000 0000 0000||||
    ------------------------||||
    0000 1111 1010 0000 0000|||
    0000 0000 0000 0000 0000|||
    ------------------------|||
    0001 1111 0100 0000 0000||
    0000 0000 0000 0000 0000||
    ------------------------||
    0011 1110 1000 0000 0000|
    0010 0000 0011 1010 0000|
    ------------------------|
    0011 1100 1000 1100 0000
    0010 0000 0011 1010 0000
    ------------------------

    This is not hard to code in a loop, but it will be one clock cycle per
    bit of precision in the result if you use a clocked process.

    I don't think there is an easy way to do division in one step. You can
    implement long division in successive conditional subtractions without
    breaking it up into clock cycles, but it will still be slow. How fast
    is your clock? A subtraction of say, 24 bits will take around 8 ns in
    many FPGAs, give or take a couple of ns. If you want a 20 bit accurate
    result it would require perhaps 160 ns to run through that many
    subtractions. This may be a bit more in order to include the delay for
    the comparison that has to be done. So a division might be done in one
    5 MHz clock if that helps...

    There are faster methods that start with an approximate result and use
    iteration to get closer to a result that is "good enough". Look up
    Newton-Raphson iteration.

    If you are dividing by a constant, it can be easier to multiply by the
    reciprocal of that constant. Then you can use multiplier hardware that
    many FPGAs have.
     
    rickman, Apr 17, 2013
    #5
  6. snake368

    Rob Gaddi Guest

    Also you might think about using some dedicated multiplier (e.g. DSP48 Macro in Xilinx devices). You can either use some IP-Generator or use a multiplier with the inverse divisor.
    So instead of A/B you calculate A*(B^-1).

    Have a nice synthesis
    Eilert[/QUOTE]

    Let me clarify that a bit, since the OP seems pretty new at this. You
    can't actually multiply by 1/132 since you don't have any way of
    representing fractional numbers.

    What you can do is multiply by (2^N / 132 / 2^(N)). Let's assume
    you're doing this on the 18 bit * 18 bit = 36 bit multipliers that are
    fairly common on FPGA architectures. What you'd want to do is multiply
    your number by (2^24 / 132) ~= 127,000. This product is nearly
    what you want, except you're high by a factor of 2^24. Fortunately,
    this is easily fixed in hardware; you simply discard the 24 least
    significant bits, leaving you the 12 uppermost, and move on.

    This entire operation can in fact be performed in one clock cycle on a
    dedicated multiplier, which the tools are smart enough to infer, and in
    fact if you convert your signals to integers you can even write it all
    on one line.

    variable my_input, my_output : integer range 0 to 2**14-1;
    ....
    -- Divide by 132.
    my_output := my_input * 127_000 / (2**24);

    Do be sure to leave that comment in, otherwise you'll never understand
    what that line is meant to accomplish 3 months from now.
     
    Rob Gaddi, Apr 17, 2013
    #6
  7. snake368

    goouse99 Guest

    Am Mittwoch, 17. April 2013 15:59:50 UTC+2 schrieb Andy:
    Hi Andi,
    integers are convenient to use when you know what you are doing.
    But for a beginner (The OP doesn't even know the concept of pipelining) it has many pitfalls.

    The obvious and easy avoidable one is the waste of bits if you are not restricting the range. Otherwise you may end up with everything done with 32 bit datawidth.

    So someone who wants to create an 8 bit counter can declare it like:
    signal count : integer range 0 to 2^8-1;
    This looks nice indeed.
    So lets do some code-reuse and scale that thing up to get a larger counter:
    signal count : integer range 0 to 2^64-1;

    You know it's crap, I know it's crap.
    What's your guess, does the OP know the reason why this is crap?

    ____________

    The tools won't care about the representation of constants in your code, that's right. But maybe the human reader cares.

    132 seems to be some average number in decimal.
    But in Binary (I reduce it to 8 bits here) it will look like this:
    10000100
    Now it becomes obvious that this number has just two '1'es.
    For some algorithms this can be quite useful.
    e.g. A multiplication is reduced to two shifts and an addition.
    Synthesis tools might recognize this or not, you only know afterwards.
    Still I admitted, that the OPs choice might be the better one for his approach.

    My reply to the OP was targeted to someone at the beginner level.
    Professionals are aware of all the bad things that may happen and avoid them blindfolded. So they have a higher degree of freedom in their coding habits.

    Have a nice synthesis
    Eilert
     
    goouse99, Apr 18, 2013
    #7
  8. snake368

    Andy Guest

    Eilert,

    Nice explanation...

    That would have been much more useful advice for the OP, a beginner, than an admonishment to use "signed/unsigned types instead of integer" with no justification.

    Andy
     
    Andy, Apr 18, 2013
    #8
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.