Comportamiento extraño al simular un decodificador usando modelsim

En mi curso introductorio de diseño de Sistemas Digitales. Nos pidieron simular un decodificador de 4 a 16 usando VHDL y modelSim. Todo compilado correctamente. Sin embargo, cuando trato de probar mi programa en el banco de pruebas, recibo un error extraño que indica que ni siquiera mi instructor pudo identificar el problema.

--Código para 4 a 16 decodificador

    library ieee;
use ieee.std_logic_1164.all;
entity decoder4to16 is
port
    (
     X: in STD_LOGIC_VECTOR(3 downto 0);
     EN: in STD_LOGIC;
     Y: out STD_LOGIC_VECTOR (0 TO 15));

     end decoder4to16;


architecture decoder4to16_arch of decoder4to16 is
signal Y_t: STD_LOGIC_VECTOR (0 TO 15);
begin
process(X,EN)
begin
case X is
when "0000" => Y_t<="0111111111111111";
when "0001" => Y_t<="1011111111111111";
when "0010" => Y_t<="1101111111111111";
when "0011" => Y_t<="1110111111111111";
when "0100" => Y_t<="1111011111111111";
when "0101" => Y_t<="1111101111111111";
when "0110" => Y_t<="1111110111111111";
when "0111" => Y_t<="1111111011111111";
when "1000" => Y_t<="1111111101111111";
when "1001" => Y_t<="1111111110111111";
when "1010" => Y_t<="1111111111011111";
when "1011" => Y_t<="1111111111101111";
when "1100" => Y_t<="1111111111110111";
when "1101" => Y_t<="1111111111111011";
when "1110" => Y_t<="1111111111111101";
when "1111" => Y_t<="1111111111111110";
when others => Y_t<="1111111111111111";
end case;
if(EN)='0' then Y<=Y_t;
else Y<="1111111111111111";
end if;
end process;
end decoder4to16_arch;

-- y aquí está el código de mi banco de pruebas

library ieee;
use ieee.std_logic_1164.all;
entity decoder4to16_tb is
end decoder4to16_tb;
architecture  decoder4to16_tb of decoder4to16_tb is
component decoder4to16
port
    (
     X: in STD_LOGIC_VECTOR(3 downto 0);
     EN: in STD_LOGIC;
     Y: out STD_LOGIC_VECTOR (0 TO 15));

     end component;
signal X: STD_LOGIC_VECTOR (3 downto 0):="0000";
signal E: STD_LOGIC:='0';
signal Y: STD_LOGIC_VECTOR (0 TO 15);
begin
U0: decoder4to16 port map (X,E,Y);
process
begin
E<='1'; X<="0000"; wait for 100 ns;
E<='0'; X<="0000"; wait for 100 ns;
X<="0001"; wait for 100 ns;
X<="0010"; wait for 100 ns;
X<="0011"; wait for 100 ns;
wait;
end process;

end decoder4to16_tb;

Ahora, la parte extraña, cuando trato de simular el banco de pruebas y mostrar las formas de onda, es como si hubiera un retraso para las entradas "0001" y superiores.

es como si hubiera algún tipo de retraso

Para asegurarse de que este "retraso" está ahí

Gracias.

Respuestas (2)

Podría hacer un mejor trabajo en su pregunta describiendo lo que está mal con los resultados, en lugar de proporcionar una imagen.

Se vuelve evidente si miras el proceso:

    process(X,EN)
    begin
        case X is
            when "0000" => Y_t<="0111111111111111";
            when "0001" => Y_t<="1011111111111111";
            when "0010" => Y_t<="1101111111111111";
            when "0011" => Y_t<="1110111111111111";
            when "0100" => Y_t<="1111011111111111";
            when "0101" => Y_t<="1111101111111111";
            when "0110" => Y_t<="1111110111111111";
            when "0111" => Y_t<="1111111011111111";
            when "1000" => Y_t<="1111111101111111";
            when "1001" => Y_t<="1111111110111111";
            when "1010" => Y_t<="1111111111011111";
            when "1011" => Y_t<="1111111111101111";
            when "1100" => Y_t<="1111111111110111";
            when "1101" => Y_t<="1111111111111011";
            when "1110" => Y_t<="1111111111111101";
            when "1111" => Y_t<="1111111111111110";
            when others => Y_t<="1111111111111111";
        end case;
        if EN ='0' then 
            Y <= Y_t;
        else 
            Y <= "1111111111111111";
        end if;
    end process;

Tiene un proceso que es sensible a Xy ENpero (con razón) no Y_t.

Las asignaciones de señales VHDL se ponen en cola en una forma de onda de salida proyectada .

Una asignación de señal puede tener la forma (IEEE Std 1076-2008, 10.5.2 Asignaciones de señales simples):

simple_waveform_assignment ::=          
    target <= [ delay_mechanism ] waveform ;

Donde una forma de onda:

waveform ::=
      waveform_element { , waveform_element }
    | unaffected

puede ser un elemento de forma de onda que puede tener una expresión posterior al tiempo :

waveform_element ::=
      value_expression [ after time_expression ]
    | null [ after time_expression ]

que especifica cuándo está disponible la actualización en el tiempo de simulación relativo. (consulte 10.5.2.2 Ejecución de una declaración de asignación simple):

El tipo base de la expresión de tiempo en cada elemento de forma de onda será el tipo físico predefinido TIME como se define en el paquete STANDARD. Si la cláusula posterior de un elemento de forma de onda no está presente, entonces se supone un " después de 0 ns" implícito. Es un error si la expresión de tiempo en un elemento de forma de onda se evalúa como un valor negativo.

Un valor de señal nunca se actualiza cuando alguna declaración de proceso está pendiente de ejecución en el ciclo de simulación actual.

Una asignación sin una expresión de tiempo posterior , lo que significa que se garantiza que la asignación al tiempo de simulación actual incurrirá en un ciclo delta (un ciclo de simulación sin un cambio en el tiempo de simulación).

Y debido a que el valor del valor recién asignado de Y_tno está disponible en el ciclo de simulación actual en el que Yestá asignado, obtendrá un retraso basado no en actualizar el valor de Y_tpero esperando hasta el final del ciclo de simulación, suspendiendo y esperando un evento en una señal en la lista de sensibilidad del proceso.

Ciclo de simulación
El estándar VHDL puede ser difícil de entender para alguien que acaba de empezar, requiere, entre otras cosas, inculcar el vocabulario de VHDL y los planes de estudios no siempre hacen eso.

Hay un par de párrafos en el libro de Peter Ashenden The Designer's Guide to VHDL que pueden ayudar a comprender lo que hace VHDL en la simulación:

La simulación comienza con una fase de inicialización, seguida de la ejecución repetitiva de un ciclo de simulación. Durante la fase de inicialización, a cada señal se le asigna un valor inicial, dependiendo de su tipo. El tiempo de simulación se establece en cero, luego se activa cada instancia de proceso y se ejecutan sus sentencias secuenciales. Por lo general, un proceso incluirá una declaración de asignación de señal para programar una transacción en una señal en algún momento de simulación posterior. La ejecución de un proceso continúa hasta que llega a una declaración de espera, lo que hace que el proceso se suspenda.

Durante el ciclo de simulación, el tiempo de simulación se adelanta primero al siguiente tiempo en el que se ha programado una transacción en una señal. En segundo lugar, se realizan todas las transacciones programadas para ese momento. Esto puede causar que ocurran algunos eventos en algunas señales. Tercero, todos los procesos que son sensibles a esos eventos se reanudan y se les permite continuar hasta que llegan a una declaración de espera y se suspenden. Una vez más, los procesos suelen ejecutar asignaciones de señales para programar más transacciones en las señales. Cuando todos los procesos han vuelto a suspenderse, se repite el ciclo de simulación. Cuando la simulación llega a la etapa en la que no hay más transacciones programadas, se detiene, ya que la simulación se completa.

Elaboración
Todo en un diseño VHDL elaborado está impulsado por eventos de señal que están programados en la forma de onda de salida proyectada para cada señal.

Las sentencias concurrentes se delegan en procesos o sentencias de bloque que proporcionan jerarquía y procesos durante la elaboración. Las llamadas a funciones son expresiones y las llamadas a procedimientos secuenciales son declaraciones. (Las sentencias de llamadas a procedimientos concurrentes se convierten en procesos que contienen sentencias de llamadas a procedimientos secuenciales cuando se elaboran).

Entonces, ¿qué significa esto para su modelo?
Los eventos de señal impulsan la ejecución del modelo. A su modelo de diseño le falta un evento de señal para Y_thacer que un proceso reanude la ejecución.

Las listas de sensibilidad en un proceso evitan la inclusión de una declaración de espera explícita e implícitamente proporcionan:

wait on sensitivity_list ;

como la última declaración del proceso, lo que significa, entre otras cosas, que la ejecución se reanuda con la primera declaración.

Ahora, si simplemente agregamos Y_ta la lista de sensibilidad, nuestro proceso tendría retroalimentación y ciclos delta potencialmente interminables porque actualizaríamos Y_tqué programa y evento sería sensible al proceso.

Para solucionar esto, puede dejar la lista de sensibilidad en paz y mover la asignación Yfuera de este proceso, o eliminar la señal Y_ty asignarla Ydirectamente en la declaración del caso que puede incrustar en la siguiente declaración if donde Y_tocurre la asignación del valor de.

Potencialmente, podría concatenar ENcon Xuna variable nombrada y requerir la posición del elemento de la variable utilizada como una expresión de declaración de caso que representa ENser '0', la otherselección cubriría todos los casos cuando Ydeberían ser todos '1' y podría eliminar la declaración if.

Eso podría verse así:

    process(X,EN)
        variable case_exp: std_logic_vector (4 downto 0);
    begin
        case_exp := EN & X;  -- EN is the leftmost position
        case case_exp is
            when "00000" => Y <= "0111111111111111";
            when "00001" => Y <= "1011111111111111";
            when "00010" => Y <= "1101111111111111";
            when "00011" => Y <= "1110111111111111";
            when "00100" => Y <= "1111011111111111";
            when "00101" => Y <= "1111101111111111";
            when "00110" => Y <= "1111110111111111";
            when "00111" => Y <= "1111111011111111";
            when "01000" => Y <= "1111111101111111";
            when "01001" => Y <= "1111111110111111";
            when "01010" => Y <= "1111111111011111";
            when "01011" => Y <= "1111111111101111";
            when "01100" => Y <= "1111111111110111";
            when "01101" => Y <= "1111111111111011";
            when "01110" => Y <= "1111111111111101";
            when "01111" => Y <= "1111111111111110";
            when others  => Y <= "1111111111111111";
        end case;
    end process;

Cuando la posición más a la izquierda de case_expes un '1' ENno es válida y Yla posición de ningún elemento es un '0'.

Esto no pretende ser una respuesta completa, pero una forma alternativa de escribir este tipo de decodificador es:

entity decoder is
  port (
    sel : in std_logic_vector (3 downto 0);
    enable : in std_logic;
    y : out std_logic_vector(15 downto 0)
  );
end decoder;

architecture Behavioral of decoder is
begin

  process (sel, enable)
  begin
    for i in y'range loop
      if (i = to_integer(unsigned(sel)) then
        y(i) <= not enable;
      else
        y(i) <= '1';
      end if;
    end loop;
  end process;

end Behavioral;

Tenga en cuenta que mi enableseñal es alta activa. Al agregar un genérico, este método le permite tener una entidad que puede funcionar como un decodificador 3:8, 4:16, 5:32, etc. También podría tener genéricos para controlar si la habilitación y la señal de salida están activas en nivel alto o activo en nivel bajo.