Ao escrever VHDL, eu recomendo usar std_logic_vector (slv) em vez de inteiro (int) para SIGNALS . (Por outro lado, usar int para genéricos, algumas constantes e algumas variáveis pode ser muito útil.) Simplificando, se você declarar um sinal do tipo int ou precisar especificar um intervalo para um número inteiro, provavelmente está fazendo algo errado.
O problema com int é que o programador VHDL não tem idéia de qual é a representação lógica interna do int e, portanto, não podemos tirar proveito disso. Por exemplo, se eu definir um int do intervalo de 1 a 10, não faço ideia de como o compilador codifica esses valores. Espero que seja codificado como 4 bits, mas não sabemos muito além disso. Se você pudesse detectar os sinais dentro do FPGA, ele poderia ser codificado como "0001" para "1010" ou codificado como "0000" para "1001". Também é possível que seja codificado de uma maneira que não faça absolutamente nenhum sentido para nós, seres humanos.
Em vez disso, devemos apenas usar slv em vez de int, porque temos controle sobre a codificação e também temos acesso direto aos bits individuais. Ter acesso direto é importante, como você verá mais adiante.
Poderíamos simplesmente lançar um int para slv sempre que precisarmos acessar os bits individuais, mas isso fica muito confuso, muito rápido. É como ter o pior dos dois mundos em vez do melhor dos dois mundos. Seu código será difícil para o compilador otimizar e quase impossível para você ler. Eu não recomendo isso.
Então, como eu disse, com slv você tem controle sobre as codificações de bits e acesso direto aos bits. Então, o que você pode fazer com isso? Vou mostrar alguns exemplos. Digamos que você precise emitir um pulso uma vez a cada 4.294.000.000 de relógios. Aqui está como você faria isso com 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;
E o mesmo código usando 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;
A maior parte desse código é idêntica entre int e slv, pelo menos no sentido do tamanho e velocidade da lógica resultante. É claro que um está contando e o outro está contando, mas isso não é importante para este exemplo.
A diferença está na "linha importante".
Com o exemplo int, isso resultará em um comparador de 32 entradas. Com LUTs de 4 entradas que o Xilinx Spartan-3 usa, isso exigirá 11 LUTs e 3 níveis de lógica. Alguns compiladores podem converter isso em uma subtração que usará a cadeia de transporte e abrangerá o equivalente a 32 LUTs, mas poderá correr mais rápido que 3 níveis de lógica.
Com o exemplo slv, não há comparação de 32 bits, portanto, são "zero LUTs, zero níveis de lógica". A única penalidade é que nosso contador é um bit extra. Como o tempo adicional para esse bit extra de contador está na cadeia de transporte, há um atraso de tempo adicional "quase zero".
Claro que este é um exemplo extremo, pois a maioria das pessoas não usaria um contador de 32 bits dessa maneira. Aplica-se a contadores menores, mas a diferença será menos dramática, embora ainda significativa.
Este é apenas um exemplo de como utilizar slv over int para obter um tempo mais rápido. Existem muitas outras maneiras de utilizar o slv - basta apenas um pouco de imaginação.
Atualização: Adicionado itens para tratar dos comentários de Martin Thompson sobre o uso de int com "if (count-1) <0"
(Nota: Eu suponho que você quis dizer "se conte <0", pois isso tornaria mais equivalente à minha versão slv e eliminaria a necessidade dessa subtração extra).
Sob algumas circunstâncias, isso pode gerar a implementação lógica pretendida, mas não é garantido que funcione o tempo todo. Depende do seu código e de como o seu compilador codifica o valor int.
Dependendo do seu compilador, e como você especifica o intervalo de seu int, é perfeitamente possível que um valor int de zero não codifique para um vetor de bits de "0000 ... 0000" quando ele entra na lógica do FPGA. Para que sua variação funcione, ela deve codificar para "0000 ... 0000".
Por exemplo, digamos que você defina um int para ter um intervalo de -5 a +5. Você espera que um valor de 0 seja codificado em 4 bits como "0000" e +5 como "0101" e -5 como "1011". Este é o esquema de codificação típico de dois complementos.
Mas não assuma que o compilador usará complemento duplo. Embora incomum, o complemento de um pode resultar em uma lógica "melhor". Ou, o compilador pode usar uma espécie de codificação "tendenciosa" onde -5 é codificado como "0000", 0 como "0101" e +5 como "1010".
Se a codificação do int estiver "correta", o compilador provavelmente inferirá o que fazer com o bit de transporte. Mas se estiver incorreto, a lógica resultante será horrível.
É possível que o uso de um int dessa maneira possa resultar em tamanho e velocidade lógicos razoáveis, mas isso não é uma garantia. Mudar para um compilador diferente (XST para Sinopse, por exemplo), ou ir para uma arquitetura FPGA diferente pode causar a coisa errada.
Não assinado / assinado vs. slv é mais um debate. Você pode agradecer ao comitê do governo dos EUA por nos dar tantas opções em VHDL. :) Eu uso slv porque esse é o padrão para interface entre módulos e núcleos. Fora isso, e alguns outros casos em simulações, acho que não há um grande benefício em usar slv em vez de assinado / não assinado. Também não tenho certeza se os sinais assinados / não assinados são compatíveis.