Convert ADC output format to DAC input

S

snake368

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?
 
G

goouse99

Am Mittwoch, 17. April 2013 00:36:48 UTC+2 schrieb (e-mail address removed):
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?

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
 
S

snake368

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

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
 
A

Andy

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
 
R

rickman

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?

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.
 
R

Rob Gaddi

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.

[snip]

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.
 
G

goouse99

Am Mittwoch, 17. April 2013 15:59:50 UTC+2 schrieb Andy:
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

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
 
A

Andy

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
 

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. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,810
Latest member
Kassie0918

Latest Threads

Top