When writing VHDL, I highly recommend using std_logic_vector (slv) instead of integer (int) for SIGNALS. (On the other hand, using int for generics, some constants, and some variables can be highly useful.) Simply put, if you declare a signal of type int, or have to specify a range for an integer then you're probably doing something wrong.
The problem with int is that the VHDL programmer has no idea what the internal logic representation of the int is, and so we cannot take advantage of it. For example, if I define an int of range 1 to 10 I have no idea how the compiler encodes those values. Hopefully it would be encoded as 4 bits, but we don't know much beyond that. If you could probe the signals inside the FPGA it might be encoded as "0001" to "1010", or encoded as "0000" to "1001". It's also possible that it is encoded in a way that makes absolutely no sense to us humans.
Instead we should just use slv instead of int, because then we have control over the encoding and also have direct access to the individual bits. Having direct access is important, as you'll see later.
We could just cast an int to slv whenever we need access to the individual bits, but that gets really messy, very fast. That's like getting the worst of both worlds instead of the best of both worlds. You're code will be difficult for the compiler to optimize, and almost impossible for you to read. I don't recommend this.
So, as I said, with slv you have control over the bit encodings and direct access to the bits. So what can you do with this? I'll show you a couple of examples. Let's say that you need to output a pulse once every 4,294,000,000 clocks. Here's how you would do this with int:
signal count :integer range 0 to 4293999999; -- a 32 bit integer
process (clk)
begin
if rising_edge(clk) then
if count = 4293999999 then -- The important line!
count <= 0;
pulse <= '1';
else
count <= count + 1;
pulse <= '0';
end if;
end if;
end process;
And the same code using slv:
use ieee.numeric_std.all;
signal count :std_logic_vector (32 downto 0); -- a 33 bit integer, one extra bit!
process (clk)
begin
if rising_edge(clk) then
if count(count'high)='1' then -- The important line!
count <= std_logic_vector(4293999999-1,count'length);
pulse <= '1';
else
count <= count - 1;
pulse <= '0';
end if;
end if;
end process;
Most of this code is identical between int and slv, at least in the sense of the size and speed of the resulting logic. Of course one is counting up and the other is counting down, but that's not important for this example.
The difference is in "the important line".
With the int example, this is going to result in a 32-input comparator. With 4-Input LUT's that the Xilinx Spartan-3 uses, this is going to require 11 LUTs and 3 levels of logic. Some compilers might convert this into a subtraction which will use the carry chain and span the equivalent of 32 LUT's but might run faster than 3 levels of logic.
With the slv example, there is no 32-bit comparison so it's "zero LUT's, zero levels of logic". The only penalty is that our counter is one extra bit. Because the additional timing for this extra bit of counter is all in the carry chain, there is "almost zero" additional timing delay.
Of course this is an extreme example, as most people wouldn't be using a 32-bit counter in this way. It does apply to smaller counters, but the difference will be less dramatic although still significant.
This is only one example of how to utilize slv over int to get faster timing. There are many other ways to utilize slv-- it only takes some imagination.
Update: Added stuff to address Martin Thompson's comments about using int with "if (count-1) < 0"
(Note: I assume you meant "if count<0", since that would make it more equivalent to my slv version and remove the need for that extra subtraction.)
Under some circumstances this might generate the intended logic implementation but it is not guaranteed to work all of the time. It will depend on your code and how your compiler encodes the int value.
Depending on your compiler, and how you specify the range of your int, it is entirely possible that an int value of zero does not encode to a bit vector of "0000...0000" when it makes it into the FPGA logic. For your variation to work, it must encode to "0000...0000".
For example, let's say you define an int to have a range of -5 to +5. You are expecting a value of 0 to be encoded into 4 bits like "0000", and +5 as "0101" and -5 as "1011". This is the typical twos-complement encoding scheme.
But don't assume that the compiler is going to use twos-complement. Although unusual, ones-complement could result in "better" logic. Or, the compiler could use a sort of "biased" encoding where -5 is encoded as "0000", 0 as "0101", and +5 as "1010".
If the encoding of the int is "correct" then the compiler will likely infer what to do with the carry bit. But if it is incorrect then the resulting logic will be horrible.
It's possible that using an int in this way could result in reasonable logic size and speed, but it is not a guarantee. Switching to a different compiler (XST to Synopsis for example), or going to an different FPGA architecture could cause the exact wrong thing to happen.
Unsigned/Signed vs. slv is yet another debate. You can thank the U.S. Government committee for giving us so many options in VHDL. :) I use slv because that is the standard for interfacing between modules and cores. Other than that, and some other cases in simulations, I don't think there is a huge benefit to using slv over signed/unsigned. I'm also not sure if signed/unsigned support tri-stated signals.