En el hilo de comentarios sobre una respuesta a esta pregunta: Resultados incorrectos en la entidad VHDL se dijo:
"Con los enteros no tienes control o acceso a la representación lógica interna en el FPGA, mientras que SLV te permite hacer trucos como utilizar la cadena de transporte de manera eficiente"
Entonces, ¿en qué circunstancias ha sido más fácil codificar usando un vector de representación de bits que usando enteros para acceder a la representación interna? ¿Y qué ventajas midió (en términos de área de chip, frecuencia de reloj, retraso u otro)?
Respuestas:
He escrito el código sugerido por otros dos carteles en ambos
vector
y eninteger
forma, teniendo cuidado de que ambas versiones funcionen de la manera más similar posible.Comparé los resultados en la simulación y luego los sinteticé usando Synplify Pro apuntando a Xilinx Spartan 6. Las muestras de código a continuación se pegan del código de trabajo, por lo que debería poder usarlas con su sintetizador favorito y ver si se comporta igual.
Contadores
En primer lugar, el conteo regresivo, como lo sugiere David Kessner:
Arquitectura vectorial:
Arquitectura entera
Resultados
En cuanto al código, el número entero me parece preferible ya que evita las
to_unsigned()
llamadas. De lo contrario, no hay mucho para elegir.Ejecutarlo a través de Synplify Pro
top := 16#7fff_fffe#
produce 66 LUT para lavector
versión y 64 LUT para lainteger
versión. Ambas versiones hacen mucho uso de la cadena de transporte. Ambos informan velocidades de reloj superiores a 280MHz . El sintetizador es bastante capaz de establecer un buen uso de la cadena de transporte: verifiqué visualmente con el visor RTL que se produce una lógica similar con ambos. Obviamente, un contador con comparador será más grande, pero eso sería lo mismo con los enteros y los vectores nuevamente.Dividiendo por 2 ** n contadores
Sugerido por ajs410:
Arquitectura vectorial
Arquitectura entera
Tienes que saltar a través de algunos aros para evitar simplemente usar
to_unsigned
y luego extraer bits que claramente producirían el mismo efecto que el anterior:Resultados
En cuanto al código, en este caso, ¡la
vector
versión es claramente mejor!En términos de resultados de síntesis, para este pequeño ejemplo, la versión entera (como predijo ajs410) produce 3 LUT adicionales como parte de los comparadores, ¡yo era demasiado optimista sobre el sintetizador, aunque está trabajando con un código terriblemente ofuscado!
Otros usos
Los vectores son una clara victoria cuando desea que la aritmética se ajuste (incluso los contadores se pueden hacer como una sola línea):
vs
aunque al menos está claro a partir de ese código que el autor tenía la intención de terminar.
Algo que no he usado en código real, pero que he meditado:
La función de "envoltura natural" también se puede utilizar para "calcular a través de desbordamientos". Cuando sabe que la salida de una cadena de sumas / restas y multiplicaciones está limitada, no tiene que almacenar los bits altos de los cálculos intermedios ya que (en el complemento de 2 s) saldrá "en el lavado" para cuando llegues a la salida. Me han dicho que este documento contiene una prueba de esto, ¡pero me pareció un poco denso hacer una evaluación rápida! Teoría de la adición de computadoras y desbordamientos - HL Garner
El uso de
integer
s en esta situación provocaría errores de simulación cuando se ajustan, aunque sabemos que al final se desenvolverán.Y como señaló Philippe, cuando necesita un número mayor que 2 ** 31 no tiene más remedio que usar vectores.
fuente
variable c : unsigned(32 downto 0);
... ¿no esc
una variable de 33 bits entonces?Al escribir VHDL, recomiendo usar std_logic_vector (slv) en lugar de entero (int) para SEÑALES . (Por otro lado, usar int para genéricos, algunas constantes y algunas variables pueden ser muy útiles). En pocas palabras, si declara una señal de tipo int o tiene que especificar un rango para un entero, probablemente esté haciendo algo mal.
El problema con int es que el programador VHDL no tiene idea de cuál es la representación lógica interna de int, por lo que no podemos aprovecharla. Por ejemplo, si defino un int de rango de 1 a 10, no tengo idea de cómo el compilador codifica esos valores. Esperemos que se codifique como 4 bits, pero no sabemos mucho más allá de eso. Si pudiera sondear las señales dentro del FPGA, podría estar codificado como "0001" a "1010", o codificado como "0000" a "1001". También es posible que esté codificado de una manera que no tiene absolutamente ningún sentido para nosotros los humanos.
En su lugar, deberíamos usar slv en lugar de int, porque entonces tenemos control sobre la codificación y también tenemos acceso directo a los bits individuales. Tener acceso directo es importante, como verá más adelante.
Podríamos lanzar un int a slv siempre que necesitemos acceso a los bits individuales, pero eso se vuelve muy complicado, muy rápido. Eso es como obtener lo peor de ambos mundos en lugar de lo mejor de ambos mundos. Su código será difícil de optimizar para el compilador y casi imposible de leer. No recomiendo esto
Entonces, como dije, con slv tienes control sobre las codificaciones de bits y acceso directo a los bits. Entonces, ¿qué puedes hacer con esto? Te mostraré un par de ejemplos. Digamos que necesita emitir un pulso una vez cada 4,294,000,000 relojes. Así es como haría esto con int:
Y el mismo código usando slv:
La mayor parte de este código es idéntico entre int y slv, al menos en el sentido del tamaño y la velocidad de la lógica resultante. Por supuesto, uno está contando y el otro está contando, pero eso no es importante para este ejemplo.
La diferencia está en "la línea importante".
Con el ejemplo int, esto dará como resultado un comparador de 32 entradas. Con las LUT de 4 entradas que usa el Xilinx Spartan-3, esto requerirá 11 LUT y 3 niveles de lógica. Algunos compiladores pueden convertir esto en una resta que utilizará la cadena de transporte y abarcará el equivalente de 32 LUT, pero podría ejecutarse más rápido que 3 niveles de lógica.
Con el ejemplo de slv, no hay comparación de 32 bits, por lo que es "cero LUT, cero niveles de lógica". La única pena es que nuestro contador es un bit extra. Debido a que el tiempo adicional para este bit adicional de contador está en la cadena de transporte, hay un retraso de tiempo adicional "casi cero".
Por supuesto, este es un ejemplo extremo, ya que la mayoría de las personas no usarían un contador de 32 bits de esta manera. Se aplica a contadores más pequeños, pero la diferencia será menos dramática aunque aún significativa.
Este es solo un ejemplo de cómo utilizar slv sobre int para obtener una sincronización más rápida. Hay muchas otras formas de utilizar slv: solo se necesita un poco de imaginación.
Actualización: Se agregaron elementos para abordar los comentarios de Martin Thompson sobre el uso de int con "if (count-1) <0"
(Nota: supongo que quiso decir "if count <0", ya que eso lo haría más equivalente a mi versión slv y eliminaría la necesidad de esa resta adicional).
En algunas circunstancias, esto podría generar la implementación lógica prevista, pero no se garantiza que funcione todo el tiempo. Dependerá de su código y de cómo su compilador codifica el valor int.
Dependiendo de su compilador y de cómo especifique el rango de su int, es completamente posible que un valor int de cero no se codifique en un vector de bits de "0000 ... 0000" cuando se convierte en la lógica FPGA. Para que su variación funcione, debe codificar a "0000 ... 0000".
Por ejemplo, supongamos que define un int para tener un rango de -5 a +5. Espera que un valor de 0 se codifique en 4 bits como "0000", y +5 como "0101" y -5 como "1011". Este es el típico esquema de codificación de dos complementos.
Pero no asuma que el compilador usará dos complementos. Aunque inusual, los complementos podrían resultar en una "mejor" lógica. O bien, el compilador podría usar una especie de codificación "sesgada" donde -5 se codifica como "0000", 0 como "0101" y +5 como "1010".
Si la codificación del int es "correcta", entonces el compilador probablemente inferirá qué hacer con el bit de acarreo. Pero si es incorrecto, entonces la lógica resultante será horrible.
Es posible que usar un int de esta manera pueda dar como resultado un tamaño lógico y una velocidad razonables, pero no es una garantía. Cambiar a un compilador diferente (XST a Sinopsis, por ejemplo), o ir a una arquitectura FPGA diferente podría causar que ocurra exactamente lo incorrecto.
Unsigned / Signed vs. slv es otro debate más. Puede agradecer al comité del gobierno de los Estados Unidos por darnos tantas opciones en VHDL. :) Uso slv porque ese es el estándar para la interfaz entre módulos y núcleos. Aparte de eso, y algunos otros casos en simulaciones, no creo que haya un gran beneficio al usar slv sobre firmado / no firmado. Tampoco estoy seguro de si las señales firmadas / no compatibles son compatibles con las señales de tres estados.
fuente
if (count-1) < 0
, creo que el sintetizador inferirá el bit de ejecución y producirá el mismo circuito que su ejemplo slv. Además, ¿no deberíamos estar usando elunsigned
tipo en estos días :)Mi consejo es probar ambos y luego mirar los informes de síntesis, mapa y lugar y ruta. Estos informes le dirán exactamente cuántas LUT consume cada enfoque, también le indicarán la velocidad máxima a la que puede operar la lógica.
Estoy de acuerdo con David Kessner en que estás a merced de tu cadena de herramientas, y no hay una respuesta "correcta". La síntesis es magia negra y la mejor manera de saber qué sucedió es leer cuidadosa y minuciosamente los informes que se producen. Las herramientas Xilinx incluso le permiten ver dentro del FPGA, hasta cómo se programa cada LUT, cómo se conecta la cadena de transporte, cómo la tela del interruptor conecta todas las LUT, etc.
Para otro ejemplo dramático del enfoque del Sr. Kessner, imagine que desea tener múltiples frecuencias de reloj en 1/2, 1/4, 1/8, 1/16, etc. Podría usar un número entero que cuente constantemente cada ciclo, y luego tener múltiples comparadores contra ese valor entero, con cada salida del comparador formando una división de reloj diferente. Dependiendo de la cantidad de comparadores, el despliegue podría ser excesivamente grande y comenzar a consumir LUT adicionales solo para el almacenamiento en búfer. El enfoque SLV simplemente tomaría cada bit individual del vector como salida.
fuente
Una razón obvia es que con signo y sin signo permiten valores mayores que el entero de 32 bits. Esa es una falla en el diseño del lenguaje VHDL, que no es esencial. Una nueva versión de VHDL podría solucionarlo, requiriendo valores enteros para admitir un tamaño arbitrario (similar al BigInt de Java).
Aparte de eso, estoy muy interesado en escuchar sobre los puntos de referencia que funcionan de manera diferente para los enteros en comparación con los vectores.
Por cierto, Jan Decaluwe escribió un buen ensayo sobre esto: Estas mentiras están hechas para contar
fuente