¿Por qué este patrón VHDL simple para un registro de desplazamiento no funciona como se esperaba?

A primera vista, esperaría que el código fuente VHDL a continuación se comporte como un registro de desplazamiento. En eso q, con el tiempo seria

"UUUU0", "UUU00", "UU000", "U0000", "00000", ....

pero en cambio siempre es Udespués de cinco (o más) ciclos de reloj consecutivos.

¿Por qué es esto?

Este código es en realidad una versión mucho más simplificada de una simulación mucho más complicada. Pero demuestra los síntomas que veo.

Muestra este resultado interesante e inesperado durante la simulación tanto en ModelSim como en ActiveHDL. No he probado otros simuladores y me gustaría (en segundo lugar una explicación de la causa) saber si otros actúan de la misma manera.

Para responder correctamente a esta pregunta debes entender que:

  • Sé que esta no es la mejor manera de implementar un registro de turnos
  • Sé que para la síntesis RTL esto debería tener un reinicio.
  • Sé que una matriz de std_logic es un std_logic_vector.
  • Conozco el operador de agregación, &.

Lo que también he encontrado:

  • Si la asignación temp(0)<='0';se mueve dentro del proceso, funciona.
  • Si el bucle está desenvuelto (ver código comentado), funciona.

Reitero que esta es una versión muy simplificada de un diseño mucho más complicado (para una CPU canalizada), configurada para mostrar únicamente los resultados inesperados de la simulación. Los tipos de señales reales son solo una simplificación. Por esta razón, debe considerar sus respuestas con el código en el formulario tal como está.

Mi conjetura es que el optimizador del motor de simulación VHDL por error (o tal vez según la especificación) no se molesta en ejecutar las expresiones dentro del ciclo ya que no hay señales externas que cambien, aunque puedo refutar esto colocando el ciclo sin envolver en un ciclo.

Así que espero que la respuesta a esta pregunta tenga más que ver con los estándares para la simulación VHDL de sintaxis VHDL inexplícita y cómo los motores de simulación VHDL hacen sus optimizaciones, en lugar de si el ejemplo de código dado es la mejor manera de hacer algo o no.

Y ahora al código que estoy simulando:

 library ieee;
 use ieee.std_logic_1164.all;   

 entity test_simple is
    port (
        clk : in  std_logic;
        q   : out std_logic
    );                   
 end entity;

 architecture example of test_simple is
    type   t_temp is array(4 downto 0) of std_logic;
    signal temp : t_temp;
 begin

    temp(0) <= '0';

    p : process (clk)
    begin               
        if rising_edge(clk) then
            for i in 1 to 4 loop
                    temp(i) <= temp(i - 1);
            end loop;

            --temp(1) <= temp(0);   
            --temp(2) <= temp(1);
            --temp(3) <= temp(2);
            --temp(4) <= temp(3);
        end if;
    end process p;
    q <= temp(4);
 end architecture;

Y el banco de pruebas:

library ieee;
use ieee.std_logic_1164.all;

entity Bench is
end entity;

architecture tb of bench is

component test_simple is
    port (
        clk : in  std_logic;
        q   : out std_logic
    );                   
end component;

signal clk:std_logic:='0';
signal q:std_logic;     
signal rst:std_logic;

constant freq:real:=100.0e3;

begin                       
    clk<=not clk after 0.5 sec / freq;

    TB:process
    begin
        rst<='1';
        wait for 10 us;
        rst<='0';
        wait for 100 us;
        wait;
    end process;

     --Note: rst is not connected
    UUT:test_simple  port map (clk=>clk,q=>q) ;
end architecture;
primero intente inicializar la temperatura en la declaración de la señal, he encontrado que los simuladores vhdl son peculiares acerca de dónde inicializa las cosas
Parecería que el simulador está ignorando la asignación concurrente temp(0)porque no hay "eventos" asociados con la constante literal. Poner la asignación dentro processcrea una asociación con los eventos del reloj que hace que funcione. Me pregunto si agregar una aftercláusula a la tarea sería una posible solución.

Respuestas (1)

Tiene que ver con lo que se puede evaluar fácilmente en el momento de la elaboración, formalmente, lo que se denomina una "expresión localmente estática". Esta es una regla de aspecto oscuro, pero merece un poco de reflexión; eventualmente tiene sentido, y su simulador es bastante correcto al alertarlo generando resultados no obvios.

Ahora, temp(1)puede evaluarse en tiempo de compilación (incluso antes del tiempo de elaboración) y puede generar un controlador en el bit 1 de "temp".

Sin embargo, temp(i)implica un poco más de trabajo para las herramientas. Dada la naturaleza trivial de los límites del bucle aquí (1 a 4), es obvio para nosotros, los humanos, que temp(0) no se puede controlar y lo que está haciendo es seguro. Pero imagine que los límites fueran funciones lower(foo) to upper(bar)en un paquete declarado en otro lugar... ahora lo máximo que puede decir con certeza es que tempestá controlado, por lo que la expresión "localmente estática" es temp.

Y eso significa que el proceso está limitado por estas reglas para controlar todo temp, momento en el que tiene varios controladores activados temp(0): el proceso de control (sin valor inicial, es decir, 'u') y el externo temp(0) <= '0';. Entonces, naturalmente, los dos controladores se resuelven en 'U'.

La alternativa sería una "pequeña regla hacky" (opinión) de que si los límites del bucle fueran constantes, haga una cosa, pero si se declararan como otra cosa, haga otra cosa, y así sucesivamente... hay, cuanto más complejo se vuelve el lenguaje... en mi opinión, no es una mejor solución.

Buena respuesta (+1), pero no estoy de acuerdo con su caracterización de "pequeña regla hacky". El objetivo de la simulación es representar el comportamiento del hardware real. Entiendo las restricciones creadas por la compilación independiente de módulos individuales, pero creo que la regla debería ser que todo lo que pueda evaluarse en tiempo de compilación debería serlo. Esta sería una regla mucho más general y ayudaría al sistema a adherirse al principio de "menor sorpresa". Permitir que las herramientas no realicen esas evaluaciones me parece más "incorrecto".
Comentario justo: Ada, por ejemplo, tiene (y expresa formalmente) mucha más complejidad sobre reglas como esta, y logra presentarnos una vista mucho más simple a los usuarios (¡sin el factor WTF de C!). VHDL se simplificó originalmente (OMI un poco demasiado lejos) de Ada. Pero tal vez podría adoptar las reglas de "congelación de tipos" de Ada que permitirían este tipo de optimización cuando es claramente seguro (como aquí) y lo prohibirían de otra manera...
Gracias Brian, lo que dices ciertamente tiene sentido. La idea de una regla simple en lugar de muchas reglas oscuras también parece tener sentido. ¿Diría que este comportamiento es verdadero (y de hecho especificado) para todos los simuladores o son solo los dos que he probado?
Si encontrara uno que hiciera algo diferente, ¡archivaría un error en su contra! Una cosa que los mayores detractores de VHDL dirán a su favor es que garantiza resultados de simulación consistentes en los casos en que otros lenguajes (no solo Verilog) no lo hacen. (aunque sí, ¡a veces sus defectos también me molestan!)
Experimento de solución rápida: si mi respuesta es correcta, puede conducir "temp(0) <= 'Z';" dentro del proceso, por lo tanto, "desconectando" el controlador fantasma, y ​​el controlador externo funcionará...
Excelente aporte, Brian. El acto de (¿inútilmente?) conducir temp(0)<='Z'dentro del proceso 'arregla' el comportamiento (si arreglar es la palabra correcta), tal vez debería ser una máscara o un engaño...
Gracias. Su función principal es ayudar a la comprensión. No es una solución horrible (¡no obtendrá un controlador tristate allí en síntesis!), Pero como señaló, hay mejores formas de expresar el problema en primer lugar ... reemplazando todo el bucle con una asignación agregada, por ejemplo .
Mi solución sería dividir "temp", que, conceptualmente, comprende datos de entrada y almacenados, en 2 partes (llamadas así por sus roles). Muy a menudo, cuando VHDL (o Ada) me sorprende de esta manera, alguna reflexión muestra que mi pensamiento de diseño estaba confuso y, como resultado, surge un diseño más limpio y simple.