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é?
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.
Ted Middleton
Ted Middleton
Stanri
Stanri