¿Por qué mi contador simple VHDL no funciona? ¿Adónde fueron mis señales?

Soy un completo principiante con VHDL y casi un principiante con lógica digital y tengo problemas para trabajar con un libro que estoy leyendo. En particular, un ejercicio pide construir un contador con un interruptor de habilitación y reinicio. Lo implementé de una manera y el compilador (Xilinx ISE) se quejó tanto que tomé el camino de menor resistencia y lo cambié a algo que pensé que no era óptimo. Pero ahora me gustaría saber qué está mal con mi código original.

Aquí está el ejemplo de 'funcionamiento':

-- EXAMPLE 1
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity clocks is
    Port ( 
           switch0 : in  STD_LOGIC;
           switch1 : in  STD_LOGIC;
           LEDs : out  STD_LOGIC_VECTOR (7 downto 0);
           clk  : in STD_LOGIC
           );
end clocks;

architecture Behavioral of clocks is
 signal counter : STD_LOGIC_VECTOR( 29 downto 0 ) := ( others => '0' );
begin

LEDs <= counter( 29 downto 22 );

clk_proc: process( clk, switch1, switch0 )
    begin
        if rising_edge( clk ) then
            if switch0 = '1' then
                counter <= counter + 1;
            elsif switch1 = '1' then
                counter <= ( others => '0' );
            end if;
        end if;
    end process;

end Behavioral;

Entonces, básicamente configuras switch0 y el contador comienza a contar. Borre switch0 y deja de contar. Establezca switch1 mientras switch0 está borrado y el contador se restablecerá a 0. Configure switch1 mientras switch0 está configurado y el contador no se restablece.

Lo que realmente quería era que el interruptor de reinicio 1 pudiera borrar el contador incluso si el interruptor 0 estaba configurado. Quería que el flanco ascendente del interruptor de reinicio 1 reiniciara el contador a 0 mientras el contador sigue subiendo si el interruptor de habilitación 0 todavía está configurado.

Pensé que podría acercarme un poco más cambiando el proceso a esto:

-- EXAMPLE 2
clk_proc: process( clk, switch1, switch0 )
    begin
        if rising_edge( clk ) then
            if switch1 = '1' then
                counter <= ( others => '0' );
            elsif switch0 = '1' then
                counter <= counter + 1;
            end if;
        end if;
    end process;

Pero extrañamente, en el chip Spartan 6 que estoy usando, ¿el interruptor de activación 0 aún anula el interruptor de reinicio 1? Entonces probé esto:

-- EXAMPLE 3
clk_proc: process( clk, switch1, switch0 )
    begin
        if rising_edge( switch1 ) then
            counter <= ( others => '0' );
        else
            if rising_edge( clk ) then
                if switch0 = '1' then
                    counter <= counter + 1;
                end if;
            end if;
        end if;
    end process;

Esto debería ser exactamente lo que quiero, pero Xilinx ISE está aún menos contento con esto: recibo estas advertencias del compilador:

WARNING:Xst:647 - Input <switch0> is never used. This port will be preserved and left unconnected if it belongs to a top-level block or it belongs to a sub-block and the hierarchy of this sub-block is preserved. 
WARNING:Xst:647 - Input <clk> is never used. This port will be preserved and left unconnected if it belongs to a top-level block or it belongs to a sub-block and the hierarchy of this sub-block is preserved.
WARNING:Xst:2404 -  FFs/Latches <counter<1:30>> (without init value) have a constant value of 0 in block <clocks>. 
WARNING:Par:288 - The signal clk_IBUF has no load.  PAR will not attempt to route this signal. 
WARNING:Par:288 - The signal switch0_IBUF has no load.  PAR will not attempt to route this signal. 
WARNING:Par:288 - The signal switch1_IBUF has no load.  PAR will not attempt to route this signal. 
WARNING:Par:283 - There are 3 loadless signals in this design. This design will cause Bitgen to issue DRC warnings.

switch0 nunca se usa? ¿Clk tampoco? ¿A dónde fueron? Y cuando lo ejecuté en mi spartan 6, hizo lo mismo que el ejemplo 'funcional'. Intenté esto también:

-- EXAMPLE 4
clk_proc: process( clk, switch1, switch0 )
    begin
        if rising_edge( switch1 ) then
            counter <= ( others => '0' );
        end if;
        if rising_edge( clk ) then
            if switch0 = '1' then
                counter <= counter + 1;
            end if;
        end if;
    end process;

Esto ni siquiera se construirá; en la implementación, recibo estas advertencias/errores:

WARNING:Xst:647 - Input <clk> is never used. This port will be preserved and left unconnected if it belongs to a top-level block or it belongs to a sub-block and the hierarchy of this sub-block is preserved.
WARNING:Xst:3002 - This design contains one or more registers/latches that are directly
   incompatible with the Spartan6 architecture. The two primary causes of this is
   either a register or latch described with both an asynchronous set and
   asynchronous reset, or a register or latch described with an asynchronous
   set or reset which however has an initialization value of the opposite 
   polarity (i.e. asynchronous reset with an initialization value of 1).

¿Alguien puede darme una pista sobre qué tienen estos procesos reelaborados que no son kosher? Entiendo que hay muchas cosas que se pueden codificar en VHDL pero que no se pueden traducir a un diseño de hardware, y la segunda de mis revisiones parece entrar en esa categoría, pero ¿alguien puede explicarme qué está pasando aquí? ? ¿Qué tiene de malo un pestillo con restablecimientos y conjuntos asíncronos? ¿Qué hay de malo con los bloques if-then-end anidados? ¿Qué estoy haciendo mal aquí?

ACTUALIZAR: Como en la respuesta, los ejemplos 3 y 4 nunca podrían haber funcionado porque un proceso no se puede cronometrar varias veces; solo puede tener una señal con un 'borde ascendente ()' o 'evento'. Resulta que el ejemplo 2 realmente funciona y hace lo que quiero que haga. Lo ejecuté a través del simulador y parecía estar bien, y luego, cuando lo sinteticé nuevamente para el hardware, funcionó bien en mi spartan 6. ¿Debo haber hecho algo extraño la primera vez que lo probé?

Respuestas (1)

el parámetro risen_edge() es solo para señales de reloj. Una opción sería la siguiente:

clk_proc: process( clk, switch1, switch0 )
begin
    if switch1 = '1' then
        counter <= ( others => '0' );
    elseif rising_edge( clk ) then
        if switch0 = '1' then
            counter <= counter + 1;
        end if;
    end if;
end process;

Esto se conoce como un proceso de reinicio asíncrono, lo que significa que reiniciará el contador cada vez que el reinicio (interruptor1) sea alto, independientemente del estado del reloj. Un proceso de reinicio síncrono se vería igual que su segundo ejemplo:

clk_proc: process( clk, switch1, switch0 )
begin
    if rising_edge( clk ) then
        if switch1 = '1' then
            counter <= ( others => '0' );
        elsif switch0 = '1' then
            counter <= counter + 1;
        end if;
    end if;
end process;

Sin embargo, indicó que su segundo ejemplo no funcionó como se esperaba. Mi sospecha es que esto se debe a que las señales que lo impulsan son interruptores en lugar de señales lógicas sincronizadas. Los interruptores en realidad no cambian instantáneamente, sino que rebotan entre encendido y apagado varias veces antes de establecerse. Debe implementar algún tipo de proceso de rebote para producir una señal que esté sincronizada con su reloj y pueda controlar su temporizador.

También vale la pena mencionar que el diseño de FPGA es mucho más fácil si primero simula las cosas. Xilinx ISE incluye un simulador: ¡cree un banco de pruebas y utilícelo! Eso le permite ver fácilmente lo que sucede en su implementación antes de que se acerque al hardware.

Ah, sí, he leído que solo se le permite una instancia de evento de 'rising_edge()' o 'en un proceso', lo que explicaría por qué las dos últimas soluciones mías no funcionaron ni funcionarán. Todavía estoy pensando en lo que es un 'proceso' en términos de circuito digital: el razonamiento que escuché fue que no puede tener 2 rise_edge () porque sería como tener dos señales de reloj para un flip-flop .
Tienes toda la razón sobre el reinicio y la habilitación: son interruptores. Gracias por la idea antirrebote. Estoy marcando esto como la respuesta.
@TedMiddleton Un proceso en términos digitales termina siendo una serie de registros en serie. Para demostrar esto, codifique un proceso y eche un vistazo al esquema RTL generado. Aquí hay un ejemplo. He marcado los procesos y también he marcado las partes asincrónicas del código. i.imgur.com/HGH1sU9.png
PD: Ese código no era el código real usado para generar el esquema, el código real usado era mucho más complicado, pero demuestra el tipo de arquitectura resultante que ves para ambos.