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 integer
s where I can, but if I explicitly want "roll-over n-bit counters", I tend to use unsigned
.
The first version is going to be more efficient in terms of area and speed-- but neither one is very good, IMHO.
A very quick way to estimate the size/speed of something is to think about each output signal and how many inputs the output is derived from. For example: a <= b xor c. We say that 'a' is a function of 2 signals (b and c). The more "inputs" there are, the more logic is required and the slower it will run. Keep in mind that this is a super rough estimate, but is useful for, well, making super rough estimates.
In version 1, you have your mymode "output" which is a function of many inputs. The important input is count1, which is a 10-bit signal. So, without considering the other signals, you can say that mymode is at least a function of 10 inputs. On the other hand, version 2 has 4 count inputs (each 10 bits), so it is a function of at least 40 inputs. That's a lot of inputs, and will create a lot of logic that runs slow.
Now, here's a super easy way to make the logic for both versions smaller and faster. For this example, I'm just going to do a simple counter that "does something" when it finishes it's count. You can adapt the same techniques to your state machine. First, here's your version:
signal count :integer range 0 to 1000;
process (clk)
begin
if rising_edge(clk) then
if load='1' then
count <= some_constant_value;
elsif count=0 then -- This line is important
do something here;
else
count <= count - 1;
end if;
end if;
end process;
And here is my version:
signal count :std_logic_vector (10 downto 0); -- Note: 1 extra bit
process (clk)
begin
if rising_edge(clk) then
if load='1' then
count <= some_constant_value - 1;
elsif count(count'high)='1' then -- This line is important
do_something_here;
else
count <= count - 1;
end if;
end if;
end process;
Your version counts from N downto 0, while mine counts from N-1 downto -1. To make this work, I made the count signal 1 bit larger and also a SLV instead of an integer. But where this really makes things faster is that your version is doing an N-bit comparison where mine is just checking a single bit. In essence, I am using the carry-chain logic from the line "count <= count - 1" to also do my comparison. The carry-chain logic is already there for the counter, I'm just making it one bit longer. Since the carry-chain logic is super fast in an FPGA, and you're already using it, the resulting logic is super small and super fast.
For our 10 bit counter, the line "elsif count=0 then" would require three 4-input LUTs and 2 levels of logic in a Xilinx Spartan-3. My version requires 1 Flip-Flop (and associated carry-chain that would have otherwise gone unused) and essentially 0 levels of logic.
But let's say that the counter was 32 bits. Your version would require 11 LUTs and 3 levels of logic. Mine would stay the same at 1 FF and 0 levels.
When you apply my method to the two versions of your state machine, each version will work. Version 1 is still the better approach, but in some circumstances you can't have a single counter and so you must use Version 2. With my method, instead of having a function of at least 40 inputs, you have a function of at least 4 inputs. 4 is much better than 40!
Best Answer
If your circuit can support delays to the trigger_en signal, you can split the comparison (for example 4 8 bits comparators) and pipeline the result over several cycles.
You can use several comparators in parallel with future values, keeping counter_a+1, counter_b+1,counter a+2, counter a+3... pipeline the result of each comparator (as above), then decide which comparison is valid from the value of count_b_en during the last 2 or 3 cycles. Lots of hardware !
"Huge combinatorial delay" : It really depends on your target frequency. FPGAs have fast carry propagation and direct paths between adjacent LUTs, so the delays are not very large for 32bits comparators. (128 bits is a large comparator, 32bits is quite narrow)
The suggestion above with lots of hardware may be theorically faster but practically slower sometimes because the additional hardware add propagation delays, signal load...