¿Operadores de cambio VHDL?

5

Todavía estoy tratando de acostumbrarme a algunos de los caprichos de VHDL y tengo un pequeño problema. En primer lugar, entiendo que los operadores de cambio como rol, ror, ssl, srl, etc. no son sintetizables. El propósito de este laboratorio es usar un modelo dorado para comparar con una versión sintetizable de lo mismo en un banco de pruebas.

Ahora, el propósito de este programa es convertir el código del termómetro en un número binario de 3 bits. Entonces, en otras palabras, el código del termómetro "00000001"="001", "00000011"="010", "00000111"="011", etc. Básicamente, estoy tratando de contar el número de 1 en la cadena de la derecha a la izquierda. No habrá ningún caso donde se coloque un '0' entre la cadena de 1, por lo que el vector "00011101" no es válido y nunca ocurrirá.

He ideado un algoritmo no sintetizable (y hasta ahora, no compilable) que no puedo averiguar cómo empezar a trabajar. Básicamente, la idea es leer el código del termómetro, desplazarlo hacia la derecha e incrementar un contador hasta que el código del termómetro sea igual a cero, y luego asignar el valor del contador al std_logic_vector de 3 bits. A continuación se muestra el código que he hecho hasta ahora.

library ieee;
use ieee.std_logic_1164.all; 
use ieee.numeric_std.all;

entity therm2bin_g is
    port(therm : inout std_logic_vector(6 downto 0); -- thermometer code
         bin : out std_logic_vector(2 downto 0); -- binary code 
         i : integer range 0 to 7);
end therm2bin_g;    

architecture behavioral_g of therm2bin_g is
begin

golden : process(therm)
begin

    while(therm /= "00000000") loop
        therm <= therm srl 1;
        i = i + 1;      
    end loop;

    bin <= std_logic'(to_unsigned(i,3));

end process golden;
behavioral_g;
    
pregunta audiFanatic

2 respuestas

8

Este código tiene algunos pequeños problemas sintácticos, pero en general no está lejos y será una forma útil de distinguir entre sintetizable y no sintetizable, ¡lo que realmente no es lo que estás esperando!

1) El mapa del puerto

port(therm : inout std_logic_vector(6 downto 0); -- thermometer code
     bin : out std_logic_vector(2 downto 0); -- binary code 
     i : integer range 0 to 7);

Puntos a tener en cuenta aquí:

  1. therm y bin tienen un modo y un tipo; i solo tiene un tipo. Como bin es solo una copia de i , realmente no necesita ambos en la lista de puertos. Puede declarar i internamente como una señal, o incluso en el proceso como una variable.
  2. bin representa un número sin firmar. Tan buen estilo es hacerlo sin firma; o incluso Integer, y transmitir más de la intención del diseño al lector. Ambos son perfectamente sintetizables. El entero puede darle un entero de 32 bits, así que hágalo bin : out natural range 0 to 7; y obtendrá un entero sin signo de 3 bits. La lección aquí es que si está realizando muchas conversiones de tipos innecesarias, DETÉNGASE: piense, y encontrará una manera de utilizar el sistema de tipos en lugar de luchar .
  3. therm ha sido declarado " inout " pero en realidad es una entrada al proceso. (El hecho de que lo estés modificando internamente es un detalle de la implementación, ¡REALMENTE no quieres exponer la versión modificada al mundo exterior!) De hecho, " inout " realmente no hace lo que crees que hace. Su propósito real es solo para buses de datos y similares, donde se comunica en ambos sentidos con la misma señal. Si lo usa como está, entonces cualquier valor que genere el valor original en therm seguirá manejando therm , y sus valores modificados entrarán en conflicto con eso para producir " X ", es decir, un valor desconocido (o en hardware real, posiblemente recalentar y quemar!).

Así que elimine i , cree bin sin firmar o un subtipo de Natural, y haga de therm una entrada.

2) Necesitas una copia local de therm que puedas modificar. Declararlo localmente como una señal.

architecture behavioral_g of therm2bin_g is
   therm_int : std_logic_vector(6 downto 0);
begin

Y i también debe declararse localmente. También podría ser una señal, pero seamos una variable en el proceso.

golden : process(therm)
variable i : integer := 0;
-- yes, it is important to initialise it!
begin

3) El bucle.

Primero debemos copiar la entrada en la señal interna, luego podemos modificarla en el bucle.

therm_int <= therm;
while (therm_int /= "00000000") loop
    therm_int <= therm_int srl 1;
    i := i + 1;      
end loop;

Para simplificarlo, podrías hacer que therm_int no esté firmado y escribir while therm_int /= 0 loop (perdiendo los paréntesis del estilo C en el proceso)

Hay otro problema aquí. Piense en un proceso como en un pequeño programa en C (pero sin algunas de las "características" más estupendas de C) - las variables funcionan más bien como espera - BUT - las señales están diseñadas esencialmente para la comunicación entre procesos en un sistema de procesamiento paralelo. Si piensas en términos de C, ¡el equivalente más cercano es una tubería que se abre en la salida estándar y se enrolla nuevamente en la entrada estándar! Puedes escribir en una tubería pero no pasa nada hasta que lo desatas ...

En VHDL, la descarga solo se producirá cuando el proceso se suspenda. Como está escrito, no puede suspenderse hasta que el bucle finalice y llegue a " end process ". Y no puede terminar el bucle hasta " therm_int = 0 ". Y no puede cambiar el valor visto en therm_int hasta que se suspenda ... esto es un bucle infinito.

Para comprender por qué las señales VHDL funcionan de esta manera, consulte esta Q & A

La forma más sencilla de evitar esto es hacer que therm_int sea otra variable de proceso y usar la asignación de variable := en lugar de la asignación de señal <= para ella.

Y ahora debería funcionar.

4) Síntesis.

¿Pero qué no es sintetizable al respecto?

Solo esto: ¡usaste while loop en lugar de for loop!

Piense en eso por un momento: generalmente no puede predecir cuántos ciclos tomará un ciclo while . Para la síntesis, eso significa que tienes que generar una cantidad desconocida de hardware!

Pero podría colocar un límite superior en las iteraciones y generar hardware para atender ese límite superior. De hecho, un bucle for se delimita automáticamente, y las herramientas de síntesis pueden manejarlo.

Así que escribe el bucle como

therm_int := therm;
for j in therm_int'range loop  
   if therm_int /= 0 then
      therm_int := therm_int srl 1;
      i := i + 1;   
   end if;   
end loop;

y sintetízalo. Notas: for j in therm_int'range loop ; porque j? ¡Bueno, no queremos ocultar tu variable i ! Y tenga en cuenta que therm_int'range equivale automáticamente a (6 downto 0) o, sin embargo, declaró therm_int . Esto protege su código contra cambios en el tamaño de therm_int o desbordamientos de búfer accidentales. Es un pequeño ejemplo de lo que quise decir con el uso del sistema de tipos en lugar de combatirlo.

Mientras estamos en ello, eliminemos el cambio, ¡lo cual es innecesario!

for j in therm_int'reverse_range loop  
   if therm_int (j) = '1' then
      i := j;   -- last assignment is most significant '1' bit
   end if;   
end loop;

Tal como está escrito, esto se sintetizará para generar suficiente hardware para implementar la tarea en un solo ciclo de reloj. En general, eso llevaría a ciclos de reloj bastante lentos, y querría racionar el trabajo (quizás una iteración de bucle único por reloj). Pero esa es otra historia ...

    
respondido por el Brian Drummond
1

Solo hay siete salidas posibles, u ocho si incluye 000, por lo que solo hay siete (ocho) entradas posibles. Solo usaría una tabla de búsqueda o una instrucción de cambio si tiene una disponible.

    
respondido por el user207421

Lea otras preguntas en las etiquetas