From you package values I generated this:
-- Field Size Contents ft_next range static?
------------------------------------------------------------------------
-- FT_INFO (3 downto 0) "0011" 35 downto 32 static
-- link_fault (2 downto 0) "000" 31 downto 29 static
-- temp_gen (2 downto 0) "011" 28 downto 26 changeable
-- switch_ID (7 downto 0) "11111111" 25 downto 18 static
-- reserved_bits (17 downto 0) (other s=> '0') 17 downto 0 static
ft_next <= FT_INFO & link_fault & temp_gen & switch_ID & reserved_bits;
There are only three non-static bits assigned to ft_next (temp_gen - possible values "100", "011" ,"010", "001" and "000").
All the rest of the locally static (known at analysis time), meaning they don't need flip flops. You ended up with three.
I'd originally gotten the order of the arguments to TO_UNISIGNED backward, but after getting that straight (and it tells you to verify everything you rely on in an answer) you notice the loop:
when compt =>
next_state <= final;
ftgen_inst: for i in NUM_PORTS -1 downto 0 loop
if (i_fault_gen(i) = '1') then
temp_gen <= std_logic_vector(to_unsigned(i,FT_INFO_BITS));
o_sel(i) <= '1';
enable <= '1';
exit ftgen_inst;
end if;
end loop; -- i
One of the characteristics of assigning signals in a process statement is that there is only one future value for a signal. It means the last fault_gen(i) with a '1' is the one that actually gets reported, with ascending priority downto 0. You might consider having temp_gen replaced by i_fault_gen should that be unacceptable.
Anyway, the way you have defined ft_next, there are only three flip flops needed, the rest of the bits are all static.