Ir al contenido principal

Reloj digital en VHDL

Si ya hemos hablado de cómo generar un reloj de 1 Hz y también hemos visto cómo controlar un display de siete segmentos con VHDL, el siguiente paso lógico es construir un circuito para un reloj digital de 24 horas.

Reloj digital VHDL
Sólo es cuestión de unir lo ya aprendido en los dos anteriores artículos y añadir algo de lógica de control.

Vamos a construir un circuito descrito con VHDL que tiene una sola entrada llamada clk50mhz, que es la señal de reloj (de 50 Mhz en este caso) que va a comandar todo el sistema. Como salida hay dos buses, uno llamado display, que obviamente se encarga de iluminar los leds correspondientes de cada display de siete segmentos, y otra salida llamada cur_display que selecciona qué display, de los cuatro posibles, se ilumina en cada momento. El código es el siguiente.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity digital_clock_top is
port (
  clk50mhz: 	in STD_LOGIC;
  display: 	out STD_LOGIC_VECTOR(7 downto 0);
  cur_display:	out STD_LOGIC_VECTOR(3 downto 0)
);
end digital_clock_top;
architecture beh of digital_clock_top is
  constant max_count: INTEGER := 25000000; -- 50000000/2
  constant max_refresh_count: INTEGER := 100000; -- 50Mzh/100000=500Hz
  signal count: INTEGER range 0 to max_count;
  signal refresh_count: INTEGER range 0 to max_refresh_count;
  signal refresh_state: STD_LOGIC_VECTOR(1 downto 0) := (others => '0');
  signal clk_state: STD_LOGIC := '0';
  signal display_sel: STD_LOGIC_VECTOR(3 downto 0) := (others => '0');
  shared variable hora1, hora2, min1, min2: INTEGER range 0 to 10 := 0;
  shared variable segundos: INTEGER range 0 to 59 := 0;
  function digito(numero:INTEGER) return STD_LOGIC_VECTOR is
	variable salida: STD_LOGIC_VECTOR(7 downto 0);
  begin
	case numero is
		when 0 => salida := "11000000"; -- 0 
		when 1 => salida := "11111001"; -- 1
		when 2 => salida := "10100100"; -- 2
		when 3 => salida := "10110000"; -- 3
		when 4 => salida := "10011001"; -- 4
		when 5 => salida := "10010010"; -- 5
		when 6 => salida := "10000011"; -- 6
		when 7 => salida := "11111000"; -- 7 
		when 8 => salida := "10000000"; -- 8
		when 9 => salida := "10010000"; -- 9
		when others => salida := "11111111";
	end case;
	return(salida);
  end digito;
	
  begin
    cur_display <= display_sel;
    gen_clock: process(clk50mhz, clk_state, count)
    begin
	if clk50mhz'event and clk50mhz='1' then
		-- contador 1HZ
		if count < max_count then 
			count <= count + 1;
		else
			clk_state <= not clk_state;
			count <= 0;
		end if;
		
		-- contador 500Hz (para refresco del display)
		if refresh_count < max_refresh_count then
			refresh_count <= refresh_count + 1;
		else
			refresh_state <= refresh_state + 1;
			refresh_count <= 0; 
                end if; 
        end if; 
    end process; 
    show_display: process(refresh_state) 
        begin -- selección del display 
            case refresh_state is 
                when "00" => display_sel <= "1110"; -- display 0 
                when "01" => display_sel <= "1101"; -- display 1 
                when "10" => display_sel <= "1011"; -- display 2 
                when "11" => display_sel <= "0111"; -- display 3 
                when others => display_sel <= "1111"; 
            end case; 
            
            -- mostrar hora 
            case display_sel is 
                when "1110" => display <= digito(hora2); -- display 0 
                when "1101" => display <= digito(hora1); -- display 1 
                when "1011" => display <= digito(min2); -- display 2 
                when "0111" => display <= digito(min1); -- display 3 
                when others => display <= "11111111"; 
	    end case;
	
	    -- parpadeo del punto
	    if display_sel="1101" then
		display(7) <= clk_state;
	    end if;
  end process;
	
  persecond: process (clk_state)
  begin
	if clk_state'event and clk_state='1' then
		
		-- contador de segundos
		if segundos < 59 then
			segundos := segundos + 1;
		else 
			segundos := 0;
			min1 := min1 + 1; -- +1 minuto
		end if;
		
		-- segundo dígito minutero
		if min1 = 10 then
			min2 := min2 + 1;
			min1 := 0;
		end if;
		
		-- primer dígito hora
		if min2 = 6 then
			hora1 := hora1 + 1;
			min2 := 0;
		end if;
		
		-- segundo dígito hora
		if hora1 = 10 then
			hora2 := hora2 + 1;
			hora1 := 0;
		end if;
			
		if hora2=2 and hora1=4 then
			hora2 := 0;
			hora1 := 0;
		end if;
	end if;
  end process;
	
end beh;
Casi todo el código ha sido ya explicado en los anteriores artículos. Se mantienen los dos contadores: el de 1 Hz que indica el paso de cada segundo, y el de 500 Hz usado para el refresco del display. Si comparas este código con los anteriores, verás que el contador de 1 Hz ahora cuenta hasta 25000000 en lugar de hasta 50000000 como en el código anterior (justo la mitad). Lo cierto es que en el código anterior creamos un reloj que cambiaba de estado cada segundo, es decir, la señal se mantenía en alto un segundo y luego en bajo otro segundo, y así sucesivamente. En otras palabras, el primer segundo empezaba a contar en el primer flanco de subida, el segundo en el siguiente flanco de bajada, y así en cada flanco de subida y de bajada. Como detectar los flancos de subida y bajada es un engorro, vamos a marcar el paso de cada segundo con el flanco de subida, así que necesitamos un contador que se active en la mitad del tiempo que necesitábamos antes. Ahora, en el tiempo en el que se produce un cambio de estado, con el nuevo contador se producen dos, por lo tanto, habrá un flanco de subida cada segundo.

Pulso de reloj

La otra parte nueva es la lógica de control de los dígitos. Se ha usado un contador para cada dígito evitando así añadir circuitos específicos para realizar operaciones matemáticas complejas. Cuando el primer contador que gestiona el primer dígito del minutero llega a 10, se vuelve a poner a cero y se suma 1 al contador que gestiona el segundo dígito del minutero, y así con el resto de dígitos. La lógico es bastante sencilla y se ve claramente en el proceso persecond del código VHDL.

Para la implementación he usado la misma placa de los artículos anteriores, basada en la FPGA EP1C3T144. Estas son las conexiones de entrada y salida en la FPGA.

reloj VHDL pin planner

Comentarios

Entradas populares de este blog

Creando firmas de virus para ClamAV

ClamAv es un antivirus opensource y multiplataforma creado por Tomasz Kojm muy utilizado en los servidores de correo Linux. Este antivirus es desarrollado por la comunidad, y su utilidad práctica depende de que su base de datos de firmas sea lo suficientemente grande y actualizado. Para ello es necesario que voluntarios contribuyan activamente aportando firmas. El presente artículo pretende describir de manera sencilla cómo crear firmas de virus para ClamAV y contribuir con ellas a la comunidad.

Ingeniería inversa de un parche de Microsoft Windows

A estas alturas ya hemos asumido que la seguridad total en Internet es, cuanto menos, un mito. Ningún software está libre de vulnerabilidades, y por mucha auditoría, test de intrusión o pruebas de fuzzing que nos empeñemos en hacer, ninguna metodología puede demostrar sin lugar a dudas que un software es 100% seguro. Ante este panorama, hay dos tendencias mayoritarias a la hora de hacer públicas las vulnerabilidades. Por un lado, están los que piensan que cuando se descubre un fallo ha de hacerse público de forma inmediata. En este escenario, la publicación de una vulnerabilidad es el pistoletazo de salida para una carrera entre el desarrollador para sacar el parche y los "malos" para lograr explotarla. Esta política es conocida como full disclosure . Por otro lado, tenemos a los siguen una política responsible disclosure , que abogan por mantener la vulnerabilidad en secreto hasta la salida del parche por parte del desarrollador. Sin entrar en cuestiones filosóficas que no...

Manejo de grafos con NetworkX en Python

El aprendizaje computacional es un área de investigación que en los últimos años ha tenido un auge importante, sobre todo gracias al aprendizaje profundo (Deep Learning). Pero no todo son redes neuronales. Paralelamente a estas técnicas, más bien basadas en el aprendizaje de patrones, también hay un auge de otras técnicas, digamos, más basadas en el aprendizaje simbólico. Si echamos la vista algunos años atrás, podemos considerar que quizá, la promesa de la web semántica como gran base de conocimiento ha fracasado, pero no es tan así. Ha ido transmutándose y evolucionando hacia bases de conocimiento basadas en ontologías a partir de las cuales es posible obtener nuevo conocimiento. Es lo que llamamos razonamiento automático y empresas como Google ya lo utilizan para ofrecerte información adicional sobre tus búsquedas. Ellos lo llaman Grafos de Conocimiento o Knowledge Graphs . Gracias a estos grafos de conocimiento, Google puede ofrecerte información adicional sobre tu búsqueda, ad...