20
\$\begingroup\$

I'm a bit confused on if I should be using integers in VHDL for synthesis signals and ports, etc.

I use std_logic at top level ports, but internally I was using ranged integers all over the place. However, I've stumbled across a few references to people saying you should only use signed/unsigned for synthesis-targeted code.

I've gone and reworked my current project to use unsigned... and, well, it's noticeably uglier.

Is it a bad practice to use integers? What's the problem? Is there some uncertainty on what width the tool will map integers to?

\$\endgroup\$
2
  • \$\begingroup\$ Good question. I've been wondering that myself. I started with using integer, positive and other types all over the place but it turned out to be very hairy to get synthesized properly. I'm hoping someone can explain why everyone ends up using std_logic in a highly typed language. \$\endgroup\$ Mar 21, 2011 at 8:55
  • 1
    \$\begingroup\$ Yeah. Isn't that crazy? Highly typed in current practice tends to result in a lot of DATA_I <= TO_UNSIGNED(32010, DATA_I'LENGTH); type stuff... that doesn't bother anyone? :) It sure seems like a lot of unnecessary baggage. (Especially when adding STD_LOGIC_VECTOR() to that) I've declared my type and size, this should be DATA_I <= 32010; That should be implicit. Going between signed/unsigned, etc can and should be be explicit... but a straight unambiguous assignment or operation on integers should be implicit. \$\endgroup\$
    – darron
    Mar 22, 2011 at 14:33

3 Answers 3

17
\$\begingroup\$

Integers are fine in synthesis, I use them all the time.

I use std_logic at top level ports, but internally I was using ranged integers all over the place

That's fine!

Be aware:

  • You are simulating first aren't you :) - Integer types don't automatically "roll-over" in simulation - it's an error to go out of the range you've specified for them. If you want roll-over behaviour, you have to code it explicitly.
  • They are only specced to go from \$-(2^{31}-1)\$ to \$+2^{31}-1\$ (i.e. not quite the full range of a 32-bit integer, you can't use \$-2^{31}\$ and remain portable), which is a bit of a pain at times. If you need to use "big" numbers, you'll have to use unsigned and signed.
  • If you don't constrain them, you can sometimes end up with 32-bit counters where less would do (if the synth and subsequent tools can't "see" that they could optimise bits away).

On the upside:

  • They are much faster to simulate than unsigned/signed vectors
  • They don't automatically roll-over in simulation (yes, it's in both lists :). This is handy - for example you get early warning that your counter is too small.

When you do use vector types, you are using ieee.numeric_std, not ieee.std_logic_arith aren't you?

I use integers where I can, but if I explicitly want "roll-over n-bit counters", I tend to use unsigned.

\$\endgroup\$
6
  • \$\begingroup\$ Yes, I use numeric_std. I guess I'm mostly worried about Xilinx tools... they still generate std_logic_vector for everything and "UNSIGNED" isn't even in the syntax highlighting. \$\endgroup\$
    – darron
    Mar 21, 2011 at 15:54
  • 1
    \$\begingroup\$ @darron Don't worry about the syntax highlighting. The editor and its syntax highlighter are a completely different piece of software from the synthesis tool. Also, unsigned is "just" a datatype. It is part of a standard library, not of the language itself. \$\endgroup\$
    – Philippe
    Mar 30, 2011 at 11:22
  • \$\begingroup\$ Isn't the lower bound -2^32 + 1 instead ? If it was -2^31 - 1 you'd need just one more bit to represent only a single number - very weird. \$\endgroup\$
    – Bregalad
    Dec 1, 2017 at 8:26
  • \$\begingroup\$ @Bregalad - good catch - that's been wrong for quite a while! \$\endgroup\$ Dec 1, 2017 at 17:17
  • \$\begingroup\$ @MartinThompson Or maybe you can write it as -(2^32-1) if you prefer to keep the minus sign. \$\endgroup\$
    – Bregalad
    Dec 1, 2017 at 17:34
8
\$\begingroup\$

Jan Decaluwe wrote an entire white paper on the problems of integers versus bit vectors. I expect his answers would be to use integers whenever possible. http://www.jandecaluwe.com/hdldesign/counting.html

\$\endgroup\$
8
\$\begingroup\$

There's nothing wrong about using integers for RTL per se, but there are reasons that some avoid it. This really is a question about subjective "best practice" and you'll eventually have to find out yourself what you prefer. As a help to that, I'll share my experience and thoughts on this.

Principally , I'm in favour of using (constrained) integers, also when writing for synthesis. I sometimes do it, but in practice, usually I stick to signed and unsigned. I'll elaborate on why.

You will be forced to use a vectorized datatypes in part of your design anyway:

  • Hardly any vendor-IP or 3rd party-IP will use integer type for ports

  • E.g. when sending data through BlockRam, even if you infer it and therefore never need to interface to any IP/macro/primitive, you'll most likely need to convert to vectorized type anyway

  • Even if neither of the above apply, you will mostly need to interface to something else at some point (a top-level port, if nothing else)

Since you can't use integer for the full design, you might want to skip it all together, because:

  • At some points, you'll need to do the conversions anyway, and this takes away part of the point of using integer in the first place

  • Also, for simulation, these conversions will typically be called with vectors of 'U' or 'X', either before reset, or at other times, and every single such function call will generate a warning messages from the package function, cluttering your simulation warnings/prompt

Drawbacks of using integer:

  • Contrary to the vectorized types, integers don't have 'U' and 'X'; I find those very helpful in simulations. You see how uninitialized signals propagate through the design, and you will probably react if you see a lot of uninitialized signals after the reset. This won't be the case if using integers.

  • With integers, there's a greater risk of simulation/synthesis mis-match when adding or subtracting resulting in under-/overflow. (As already pointed out by someone else.)

Typical cases where I find integer to really be a good option:

  • For debug signals/counters that you monitor through chipScope/signalTap etc.

  • Totally internal representation of counters, that never go into or out of your own code. Yes, there are such cases, e.g. if you're writing a FIFO and you are dead-reckoning writes/reads to form the signals full, empty, almostFull etc. (however arithmetics on the pointers is a better way than dead-reckoning in this case...)

My own conclusions: I do use integers sometimes, but sparingly, and mostly in the cases described above. I don't see much overhead in using unsignedand signed instead of integer, and therefore, usually stick to them.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.