No tópico de comentário de uma resposta a esta pergunta: Saídas incorretas na entidade VHDL , foi afirmado:
"Com números inteiros, você não tem controle ou acesso à representação lógica interna no FPGA, enquanto o SLV permite fazer truques como utilizar a cadeia de transporte com eficiência"
Então, em que circunstâncias você achou mais legal codificar usando um vetor de representação de bits do que usar um número inteiro s para acessar a representação interna? E que vantagens você mediu (em termos de área de chip, frequência de clock, atraso ou outros fatores)?
Respostas:
Escrevi o código sugerido por outros dois pôsteres, tanto na forma
vector
quanto nainteger
forma, cuidando para que as duas versões operem da maneira mais semelhante possível.Comparei os resultados na simulação e depois sintetizei usando o Synplify Pro, direcionando o Xilinx Spartan 6. As amostras de código abaixo são coladas a partir do código de trabalho, para que você possa usá-las com seu sintetizador favorito e ver se ele se comporta da mesma maneira.
Downcounters
Primeiro, o oponente, como sugerido por David Kessner:
Arquitetura vetorial:
Arquitetura inteira
Resultados
Em termos de código, o número inteiro parece preferível para mim, pois evita as
to_unsigned()
chamadas. Caso contrário, não há muito a escolher.A execução no Synplify Pro
top := 16#7fff_fffe#
produz 66 LUTs para avector
versão e 64 LUTs para ainteger
versão. Ambas as versões fazem muito uso da cadeia de transporte. Ambos relatam velocidades de clock superiores a 280 MHz . O sintetizador é capaz de estabelecer um bom uso da cadeia de transporte - verifiquei visualmente com o visualizador de RTL que lógica semelhante é produzida com ambos. Obviamente, um contador com comparador será maior, mas seria o mesmo com números inteiros e vetores novamente.Dividindo por 2 ** n contadores
Sugerida por ajs410:
Arquitetura vetorial
Arquitetura inteira
Você precisa pular algumas etapas para evitar o uso
to_unsigned
e a retirada de bits que claramente produziriam o mesmo efeito acima:Resultados
Em termos de código, neste caso, a
vector
versão é claramente melhor!Em termos de resultados de síntese, para este pequeno exemplo, a versão inteira (como o ajs410 previu) produz 3 LUTs extras como parte dos comparadores; fiquei otimista demais com o sintetizador, embora ele esteja trabalhando com um pedaço de código muito ofuscado!
Outros usos
Os vetores são uma vitória clara quando você deseja aritmética (os contadores podem ser feitos como uma única linha):
vs
embora pelo menos fique claro a partir desse código que o autor pretendia desvendar.
Algo que não usei em código real, mas ponderei:
O recurso "empacotamento natural" também pode ser utilizado para "computação através de estouros". Quando você sabe que a saída de uma cadeia de adições / subtrações e multiplicações é limitada, você não precisa armazenar os bits altos dos cálculos intermediários, pois (no complemento de 2 s) sairá "na lavagem" no momento em que você chegar à saída. Disseram-me que este artigo contém uma prova disso, mas me pareceu um pouco denso fazer uma avaliação rápida! Teoria da adição e do excesso de computador - HL Garner
Usar
integer
s nessa situação causaria erros de simulação quando eles fossem quebrados, mesmo sabendo que eles serão desembrulhados no final.E, como Philippe apontou, quando você precisa de um número maior que 2 ** 31, não tem escolha a não ser usar vetores.
fonte
variable c : unsigned(32 downto 0);
... então não éc
uma variável de 33 bits?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:
E o mesmo código usando slv:
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.
fonte
if (count-1) < 0
, acho que o sintetizador inferirá o bit de execução e produzirá o mesmo circuito do seu exemplo slv. Além disso, não deveríamos estar usando ounsigned
tipo estes dias :)Meu conselho é tentar os dois e depois analisar os relatórios de síntese, mapa e localização e rota. Esses relatórios informarão exatamente quantas LUTs cada abordagem está consumindo; também informarão a velocidade máxima na qual a lógica pode operar.
Eu concordo com David Kessner que você está à mercê de sua cadeia de ferramentas e não há uma resposta "certa". A síntese é magia negra e a melhor maneira de saber o que aconteceu é ler com cuidado e profundidade os relatórios produzidos. As ferramentas Xilinx até permitem ver dentro do FPGA, até como cada LUT é programada, como a cadeia de transporte está conectada, como a estrutura do comutador conecta todos os LUTs, etc.
Para outro exemplo dramático da abordagem do Sr. Kessner, imagine que você deseja ter várias frequências de clock em 1/2, 1/4, 1/8, 1/16 etc. Você pode usar um número inteiro que conta constantemente todos os ciclos, e, em seguida, tenha vários comparadores em relação a esse valor inteiro, com cada saída do comparador formando uma divisão de relógio diferente. Dependendo do número de comparadores, o fanout pode se tornar excessivamente grande e começar a consumir LUTs extras apenas para buffer. A abordagem SLV levaria apenas cada bit individual do vetor como saída.
fonte
Uma razão óbvia é que assinados e não assinados permitem valores maiores que o número inteiro de 32 bits. Essa é uma falha no design da linguagem VHDL, que não é essencial. Uma nova versão do VHDL poderia corrigir isso, exigindo valores inteiros para suportar tamanho arbitrário (semelhante ao BigInt do Java).
Fora isso, estou muito interessado em saber sobre benchmarks com desempenho diferente para números inteiros em comparação com vetores.
Por outro lado, Jan Decaluwe escreveu um belo ensaio sobre o assunto: Estas entradas são feitas para a Countin '
fonte