Sunteți pe pagina 1din 36

library IEEE;

use IEEE.STD_LOGIC_1164.ALL;

use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity compuertas is

Port ( X : in STD_LOGIC;

Y : in STD_LOGIC;

Z : out STD_LOGIC_VECTOR (7 downto 0));

end compuertas;

architecture Behavioral of compuertas is

begin

Z(7)<= not X;

Z(6)<= not Y;

Z(5)<= X and Y;

Z(4)<= X or Y;

Z(3)<= X xor Y;

Z(2)<= X nand Y;

Z(1)<= X nor Y;

Z(0)<= X xnor Y;

end Behavioral;
Reloj de 12hrs en VHDL
Objetivo
Diseñar un reloj de tiempo real, donde cada segundo del reloj sea un segundo real. El reloj debe
ser de 24 horas.

Material
 6 Display ánodo común de 12.7mm modelo DA05
 6 Resistencia de 220Ω
 Batería 9V
 1 Oscilador de cristal con salida de señal cuadrada
 Fuente de 5V
 Circuito integrado 7448
 CPLD
 Max-Plus II

Procedimiento
El primer paso para lograr nuestro objetivo es la programación en VHDL en Max-Plus II.
Obteniendo los siguientes programas.
Este programa es un divisor de frecuencia el cual de la frecuencia de 4Mhz que se recibe del
oscilador se obtiene una salida de 1 Mhz necesaria para nuestro propósito. El cual puede ser
modificado si el reloj no va en el tiempo adecuado.
------------------------------------------------------------------------------------------------------------

--DIVISOR DE FRECUENCIA
LIBRARY ieee;
USE ieee.std_logic_arith.all;
USE ieee.std_logic_1164.all;

ENTITY clk_seg IS
PORT(
clock : IN BIT;
Segundo : OUT BIT
);
END clk_seg;

ARCHITECTURE a OF clk_seg IS
BEGIN PROCESS (clock)
VARIABLE CONTEO_MAX: INTEGER;
BEGIN
IF (clock = '1' AND clock'event) THEN
CONTEO_MAX := CONTEO_MAX+1;
END IF;
IF (CONTEO_MAX > 39) THEN
segundo<= '1';
ELSE IF(CONTEO_MAX=40) THEN
CONTEO_MAX:=0;
segundo<='1';
ELSE
segundo<='0';
END IF;
END IF;
END PROCESS;
END a;
------------------------------------------------------------------------------------------------------------
Luego se debe de hacer un programa para que cuente de 0 a 9 que es el caso e las unidades

--CONTEO DE UNIDADES
ENTITY MOD_10 IS
PORT(
clock :IN BIT;
CONTEO_MAX,
q0, q1, q2, q3: OUT BIT);
END MOD_10;

ARCHITECTURE a OF MOD_10 IS
BEGIN PROCESS (clock)
VARIABLE count: BIT_VECTOR(3 DOWNTO 0);
VARIABLE x1: BIT;
BEGIN IF (clock = '1' AND clock'event) THEN
CASE count IS
WHEN "0000"=> count:="0001"; x1:='0';
WHEN "0001"=> count:="0010"; x1:='0';
WHEN "0010"=> count:="0011"; x1:='0';
WHEN "0011"=> count:="0100"; x1:='0';
WHEN "0100"=> count:="0101"; x1:='0';
WHEN "0101"=> count:="0110"; x1:='0';
WHEN "0110"=> count:="0111"; x1:='0';
WHEN "0111"=> count:="1000"; x1:='0';
WHEN "1000"=> count:="1001"; x1:='0';
WHEN OTHERS=> count:="0000"; x1:='1';
END CASE;
END IF;
CONTEO_MAX<=x1;
q0 <= count(0);
q1 <= count(1);
q2 <= count(2);
q3 <= count(3);
END PROCESS;
END a;
------------------------------------------------------------------------------------------------------------
Ahora un programa para que cuente las decenas
------------------------------------------------------------------------------------------------------------
--CONTEO DE DECENAS
ENTITY MOD_6 IS
PORT(
clock :IN BIT;
CONTEO_MAX, q0, q1, q2, q3: OUT BIT
);
END MOD_6;
ARCHITECTURE a OF MOD_6 IS
BEGIN PROCESS (clock)
VARIABLE count: BIT_VECTOR(3 DOWNTO 0);
VARIABLE x1: BIT;
BEGIN
IF (clock = '1' AND clock'event) THEN
CASE count IS
WHEN "0000"=> count:="0001"; x1:='0';
WHEN "0001"=> count:="0010"; x1:='0';
WHEN "0010"=> count:="0011"; x1:='0';
WHEN "0011"=> count:="0100"; x1:='0';
WHEN "0100"=> count:="0101"; x1:='0';
WHEN OTHERS=> count:="0000"; x1:='1';
END CASE;
END IF;
CONTEO_MAX<=x1;
q0 <= count(0);
q1 <= count(1);
q2 <= count(2);
q3 <= count(3);
END PROCESS;
END a;
------------------------------------------------------------------------------------------------------------
Al final por cuestiones de espacio colocamos uno doble para el conteo de 24 horas
------------------------------------------------------------------------------------------------------------
ENTITY MOD_12 IS
PORT(
clock :IN BIT;
q0, q1, q2, q3, d0, d1, d2, d3: OUT BIT);
END MOD_12;

ARCHITECTURE a OF MOD_12 IS
BEGIN PROCESS (clock)
VARIABLE count, unit, dec: BIT_VECTOR(3 DOWNTO 0);
VARIABLE x1: BIT;

BEGIN IF (clock = '1' AND clock'event) THEN


CASE count IS
WHEN "0000"=> count:="0001"; unit:="0001";
dec:="0000";
WHEN "0001"=> count:="0010"; unit:="0010";
dec:="0000";
WHEN "0010"=> count:="0011"; unit:="0011";
dec:="0000";
WHEN "0011"=> count:="0100"; unit:="0100";
dec:="0000";
WHEN "0100"=> count:="0101"; unit:="0101";
dec:="0000";
WHEN "0101"=> count:="0110"; unit:="0110";
dec:="0000";
WHEN "0110"=> count:="0111"; unit:="0111";
dec:="0000";
WHEN "0111"=> count:="1000"; unit:="1000";
dec:="0000";
WHEN "1000"=> count:="1001"; unit:="1001";
dec:="0000";
WHEN "1001"=> count:="1010"; unit:="0000";
dec:="0001";
WHEN "1010"=> count:="1011"; unit:="0001";
dec:="0001";
WHEN OTHERS=> count:="0000"; unit:="0000";
dec:="0000";
END CASE;
END IF;
q0 <= unit(0);
q1 <= unit(1);
q2 <= unit(2);
q3 <= unit(3);
d0 <= dec(0);
d1 <= dec(1);
d2 <= dec(2);
d3 <= dec(3);

END PROCESS;
END a;
------------------------------------------------------------------------------------------------------------
Luego se utilizo un multiplexor
------------------------------------------------------------------------------------------------------------
LIBRARY ieee;
USE ieee.std_logic_arith.all;
USE ieee.std_logic_1164.all;

ENTITY clk_multiplex IS
PORT(
clock, su0,su1,su2,su3
,sd0,sd1,sd2,sd3
,mu0,mu1,mu2,mu3,md0,md1,md2,md3,hu0,hu1,hu2,hu3,hd0,hd
1,hd2,hd3
:IN BIT;
s0, s1, s2, s3 : OUT BIT
);
END clk_multiplex;

ARCHITECTURE a OF clk_multiplex IS
BEGIN PROCESS (clock)
VARIABLE count : BIT_VECTOR(2 DOWNTO 0);
VARIABLE CONTEO_MAX: INTEGER;
VARIABLE result: BIT_VECTOR(3 DOWNTO 0);
BEGIN
IF (clock = '1' AND clock'event) THEN
CONTEO_MAX := CONTEO_MAX+1;
END IF;
IF (CONTEO_MAX = 1) THEN
CONTEO_MAX:= 0;
CASE count IS
WHEN "000" => count:="001";
result:=sd0&sd1&sd2&sd3;
WHEN "001" => count:="010";
result:=mu0&mu1&mu2&mu3;
WHEN "010" => count:="011";
result:=md0&md1&md2&md3;
WHEN "011" => count:="100";
result:=hu0&hu1&hu2&hu3;
WHEN "100" => count:="101";
result:=hd0&hd1&hd2&hd3;
WHEN OTHERS=> count:="000";
result:=su0&su1&su2&su3;
END CASE;
END IF;
s0 <= result(0);
s1 <= result(1);
s2 <= result(2);
s3 <= result(3);
END PROCESS;
END a;
------------------------------------------------------------------------------------------------------------
El cual recibe 4 bit de cada programa dando un total de 25 entradas y los combina para
obtener 4 salidas las cuales se colocaran a un 7448 para obtener 6 salidas una correspondiente
para cada display.

Todo bien hasta aquí. Ahora el problema que surge es el siguiente.

El programa no cabe en nuestro CPLD al parecer el modelo Altera EPM3064ALC44-10N


solamente tiene 64 celdas lógicas y el programa necesita 103.
Es aquí donde nos hemos estancado ya que no nos es posible programar.
Es importante mencionar que los WaveForm fuero satisfactorios también.
Pero Teoricamente la programacion es correcta solamente se necesita un CPLD con mayor
capacidad para probarlo

El contador
La entidad del contador se describe en la figura 2, donde se observa que
solamente tiene dos entradas: el reloj de 1 Hz (o 1 segundo) y la señal de reset.
Las salidas corresponden a los cuatro digitos: dos para mostrar la hora y dos para
mostrar los minutos.
Figura 2: Entradas y salidas del contador.
Existen diversas formas para realizar el contador del reloj, todas con ventajas y
desventajas. En esta entrada se optó por usar cuatro contadores independientes
(uno por dígito) para evitar posteriores conversiones de números binarios a BCD.
El listado 1 muestra el código utilizado para crear el contador para nuestro reloj.

Listado 1: Código del contador de horas y minutos.

1 library IEEE;

2 use IEEE.NUMERIC_STD.ALL;

3 use IEEE.STD_LOGIC_1164.ALL;

4
entity contador_reloj is
5
PORT (
6
clk : IN STD_LOGIC; --Reloj de 1Hz.
7
reset: IN STD_LOGIC; --Señal de reset.
8
H1 : OUT STD_LOGIC_VECTOR(2 DOWNTO 0); --Segundo digito de la hora.
9
H0 : OUT STD_LOGIC_VECTOR(3 DOWNTO 0); --Primer digito de la hora.
10
M1 : OUT STD_LOGIC_VECTOR(2 DOWNTO 0); --Segundo digito de los minutos.
11 M0 : OUT STD_LOGIC_VECTOR(3 DOWNTO 0) --Primer digito de los minutos.
12 );
13 end contador_reloj;

14

15 architecture Behavioral of contador_reloj is


16 signal mm1: UNSIGNED(2 downto 0) := "000" ;

17 signal mm0: UNSIGNED(3 downto 0) := "0000";

signal hh1: UNSIGNED(2 downto 0) := "000" ;


18
signal hh0: UNSIGNED(3 downto 0) := "0000";
19
begin
20
reloj: process (clk, reset) begin
21
if reset = '1' then
22 hh1 <= "000" ;
23 hh0 <= "0000";

24 mm1 <= "000" ;

25 mm0 <= "0000";

26 elsif rising_edge(clk) then

mm0 <= mm0 + 1;


27
if mm0 = 9 then
28
mm1 <= mm1 + 1;
29
mm0 <= "0000";
30
end if;
31 -- Al pasar 59 minutos, contar una hora.
32 if mm1 = 5 AND mm0 = 9 then

33 hh0 <= hh0 + 1;

34 mm1 <= "000";

35 end if;

if hh0 = 9 then
36
hh1 <= hh1 + 1;
37
hh0 <= "0000";
38
end if;
39
-- Al pasar 23:59, regresar a 00:00.
40 if hh1 = 2 AND hh0 = 3 AND mm1 = 5 AND mm0 = 9 then
41 hh1 <= "000";

42 hh0 <= "0000";

43 end if;

end if;
44 end process;

45

46 --Asignación de señales.

H1 <= STD_LOGIC_VECTOR(hh1);
47
H0 <= STD_LOGIC_VECTOR(hh0);
48
M1 <= STD_LOGIC_VECTOR(mm1);
49
M0 <= STD_LOGIC_VECTOR(mm0);
50
end Behavioral;
51

52

53

54

55

56

Las líneas 5 a 14 contienen la definición de la entidad del contador del reloj,


listando sus entradas y salidas. Los contadores internos a utilizar se declaran en
las líneas 17 a 20. El proceso reloj es bastante simple:

 El reset se encarga de regresar el contador a cero (líneas 23 a 27).


 El contador mm0 cuenta de 0 a 9, incrementa en uno cada vez que recibe
la señal de reloj.
 El contador mm1 cuenta de 0 a 5, incrementa en uno cada vez que mm0 es
nueve.
 El contador hh0 cuenta de 0 a 9, incrementa en uno cada vez que mm1 es
5 y mm0 es 9 (cada hora).
 El contador hh0 cuenta de 0 a 2, incrementa en uno cada vez que hh0 es
nueve.
 Los contadores de hora, hh0 y hh1, regresan a cero cuando la hora es
23:59.
El divisor de frecuencia a 1 Hz
El divisor a 1Hz es similar al divisor a 200Hz, sólo es necesario cambiar la
constante que sirve para conmutar la señal de salida. Los cálculos para dividir la
frecuencia de entrada de 50 MHz a 1 Hz son:

contador1Hz=50MHz1Hz=50000000

Como sabemos, el reloj tiene el mismo tiempo en alto que en bajo:

talto=tbajo=500000002=25000000
Y dado que empezamos el conteo desde cero, nuestra nueva constante para el
reloj es 24 999 999.

(Aunque es sólo copiar el módulo del divisor de 200Hz y cambiar unas cuantas
cosas, aquí dejo el código correspondiente al divisor de 1 Hz).

Listado 2: Divisor de frecuencia de 50MHz a 1Hz.

Uniéndolo todo
Ahora que ya tenemos todos los bloques necesarios, sólamente falta enlazarlos.
Esto se realiza mediante un nuevo archivo, denominado reloj.vhd, que contiene
los PORT MAP correspondientes, como se muestra en el listado 3.

Listado 3: Módulo principal del reloj.

1 library IEEE;
2 use IEEE.STD_LOGIC_1164.ALL;

4 entity reloj is

5 PORT(
6 clk : IN STD_LOGIC;

7 reset : IN STD_LOGIC;

8 salida: OUT STD_LOGIC_VECTOR(7 downto 0);

9 MUX : OUT STD_LOGIC_VECTOR(3 downto 0)

);
10
end reloj;
11

12
architecture Behavioral of reloj is
13
COMPONENT clk1Hz IS
14
PORT (
15
entrada: IN STD_LOGIC;
16
reset : IN STD_LOGIC;
17 salida : OUT STD_LOGIC
18 );
19 END COMPONENT;

20

21 COMPONENT contador_reloj IS

22 PORT (

23 clk : IN STD_LOGIC;

24 reset: IN STD_LOGIC;

H1 : OUT STD_LOGIC_VECTOR(2 DOWNTO 0);


25
H0 : OUT STD_LOGIC_VECTOR(3 DOWNTO 0);
26
M1 : OUT STD_LOGIC_VECTOR(2 DOWNTO 0);
27
M0 : OUT STD_LOGIC_VECTOR(3 DOWNTO 0)
28
);
29 END COMPONENT;
30

31 COMPONENT siete_segmentos_completo IS
32 PORT (

33 clk : IN STD_LOGIC;
34 reset : IN STD_LOGIC;

35 D0 : IN STD_LOGIC_VECTOR(5 downto 0);

36 D1 : IN STD_LOGIC_VECTOR(5 downto 0);

37 D2 : IN STD_LOGIC_VECTOR(5 downto 0);

38 D3 : IN STD_LOGIC_VECTOR(5 downto 0);

39 salida: OUT STD_LOGIC_VECTOR(7 downto 0);

40 MUX : OUT STD_LOGIC_VECTOR(3 downto 0)

);
41
END COMPONENT;
42

43
signal clk_out : STD_LOGIC := '0';
44
signal HH1, MM1: STD_LOGIC_VECTOR(2 downto 0);
45
signal HH0, MM0: STD_LOGIC_VECTOR(3 downto 0);
46 signal pHH1, pHH0, pMM1, pMM0: STD_LOGIC_VECTOR(5 downto 0);
47 begin
48 --PORT MAPs necesarios para habilitar el reloj.

49 clk_i: clk1Hz PORT MAP(clk, reset, clk_out);

50 cnt_i: contador_reloj PORT MAP(clk_out, reset, HH1, HH0, MM1, MM0);

seg_i: siete_segmentos_completo PORT MAP(clk, reset, pMM0, pMM1, pHH0, pHH1, sa


51

52
--Padding de las señales del contador para siete segmentos.
53
pHH1 <= "000" & HH1;
54
pHH0 <= "00" & HH0;
55
pMM1 <= "000" & MM1;
56
pMM0 <= "00" & MM0;
57
end Behavioral;
58

59

60

61
Implementación en Basys2
Para probar nuestro gran diseño digital en la tarjeta Basys2 es necesario crear un
archivo deconstraints que enlacen las señales de salida a puertos en el FPGA. El
contenido del archivo se muestra en el listado 4 mientras que en la figura 3 se
muestra el resultado de dicha implementación.

Listado 4: Archivo de constraints para el reloj implementado en Basys2

1 NET "clk" LOC = "B8";

2 NET "reset" LOC = "G12";

3 # Segmentos del visualizador. #########

4 NET "salida<7>" LOC = "N13"; # dp

5 NET "salida<6>" LOC = "M12"; # g

6 NET "salida<5>" LOC = "L13"; # f

7 NET "salida<4>" LOC = "P12"; # e

8 NET "salida<3>" LOC = "N11"; # d

9 NET "salida<2>" LOC = "N14"; # c

10 NET "salida<1>" LOC = "H12"; # b

11 NET "salida<0>" LOC = "L14"; # a

12 # Multiplexor #########################

13 NET "MUX<3>" LOC = "F12";

14 NET "MUX<2>" LOC = "J12";

15 NET "MUX<1>" LOC = "M13";

16 NET "MUX<0>" LOC = "K14";


Figura 3: Reloj en tarjeta Basys2.
El error
Sí has seguido el proyecto hasta este punto puede que ya te hayas dado cuenta de
un pequeño error: el reloj cuenta los minutos y las horas, pero el contador de
minutos se basa en un reloj que marca los segundos (¡Oh, no!). La pregunta es,
¿cómo solucionarías este problema? ¿qué alternativas existen para que el
contador de minutos cuente, efectivamente, minutos?

Está bien, pero...


... ¿cómo establezco la hora inicial? Hasta ahora, la única forma de poner la hora
correcta es reiniciar el contador a media noche... lo cual no es exactamente
óptimo. Dejaremos la función de establecer la hora y la correción del contador
para la próxima entrada. Me gustaría escuchar sugerencias para resolver los
problemas mencionados.

Divisor de frecuencia con VHDL


2012/07/26 por Carlos Ramos 51 comentarios


 0
inShare

En este breve artículo se describe un divisor de frecuencia con VHDL, así como
el proceso mediante el cual se obtiene el factor de escalamiento deseado.

Por cierto, existe un generador de divisor de frecuencia basado en


contadores basado en la información de este artículo (fase beta). Y, en caso de
que necesites hacer muchos divisores de frecuencia, puedes probar la herramienta
de cálculo de escalas para divisores de frecuenciapara obtener los valores que
necesitas en los contadores (también en fase beta).

Divisor de frecuencia de 50MHz a 200Hz

Descargar 1.07 KB

Los cálculos
El divisor de frecuencia es un componente simple, cuyo objetivo es reducir la
frecuencia de entrada. Éste se implementa con ayuda del factor de escalamiento y
un contador. Primeramente, el factor de escalamiento es la relación entre la
frecuencia de entrada y la frecuencia de salida deseada:

Escala=fentradafdeseada
Asumiendo que tenemos una frecuencia de 50MHz y deseamos una salida
de 200Hz, tenemos que:

Escala=50MHz200Hz=250000
Por lo tanto, el contador para el divisor de frecuencia tiene como función generar
la señal de salida de 200Hz cada 250000 ciclos.

El código
Sin más preámbulo, el Listado 1 muestra el código fuente para el divisor de
frecuencia.

Listado 1: Divisor de frecuencia con reset asíncrono.

1 library IEEE;

use IEEE.STD_LOGIC_1164.ALL;
2

3
entity clk200Hz is
4
Port (
5
entrada: in STD_LOGIC;
6
reset : in STD_LOGIC;
7
salida : out STD_LOGIC
8
);
9 end clk200Hz;
10

11 architecture Behavioral of clk200Hz is


12 signal temporal: STD_LOGIC;

13 signal contador: integer range 0 to 124999 := 0;

14 begin

divisor_frecuencia: process (reset, entrada) begin


15
if (reset = '1') then
16
temporal <= '0';
17
contador <= 0;
18
elsif rising_edge(entrada) then
19 if (contador = 124999) then
20 temporal <= NOT(temporal);
21 contador <= 0;

22 else

contador <= contador+1;


23
end if;
24
end if;
25
end process;
26

27
salida <= temporal;
28 end Behavioral;
29

30

31

Las líneas 1 y 2 equivalen a los #include de C/C++ o los import de Python.


Entre las líneas 4 y 10 se declaran todas las entradas y salidas del sistema.

El proceso divisor_frecuencia, líneas 16 a 28, se encarga de generar la


señal de 200Hz por medio de un contador de 0 a 124999. ¿Porqué 124999 y no
250000? Una señal de reloj se mantiene el mismo tiempo en alto que en bajo;
para este caso en particular, 125000 ciclos en alto y 125000 ciclos en bajo. Dado
que comenzamos a contar desde cero, el límite superior es 125000-1.

La señal de reset sirve para reiniciar el contador y es una señal indispensable en


cualquier sistema digital.

La simulación
Con ayuda del software de Xilinx ISE (versión 13.1), se genera un test bench o
banco de pruebas cuyo objetivo es demostrar la veracidad de lo que acábo de
decir (una simulación). Por fortuna el software hace la mayoría de la tarea, de
igual forma el código se muestra en el listado 2.
Listado 2: Banco de pruebas del divisor de frecuencia.
LIBRARY ieee;
1
USE ieee.std_logic_1164.ALL;
2

3
ENTITY clk200Hz_tb IS
4
END clk200Hz_tb;
5

6 ARCHITECTURE behavior OF clk200Hz_tb IS


7 COMPONENT clk200Hz
8 PORT(

9 entrada : IN std_logic;

10 reset : IN std_logic;

11 salida : OUT std_logic

12 );

END COMPONENT;
13

14
-- Entradas
15
signal entrada : std_logic := '0';
16
signal reset : std_logic := '0';
17
-- Salidas
18 signal salida : std_logic;
19 constant entrada_t : time := 20 ns;
20 BEGIN

21 -- Instancia de la unidad bajo prueba.

22 uut: clk200Hz PORT MAP (

entrada => entrada,


23
reset => reset,
24
salida => salida
25
);
26

27
-- Definición del reloj.
28 entrada_process :process

29 begin

entrada <= '0';


30
wait for entrada_t / 2;
31
entrada <= '1';
32
wait for entrada_t / 2;
33
end process;
34

35 -- Procesamiento de estímulos.
36 estimulos: process
37 begin

38 reset <= '1'; -- Condiciones iniciales.

39 wait for 100 ns;

reset <= '0'; -- ¡A trabajar!


40
wait;
41
end process;
42
END;
43

44

45

46

47

Para terminar, una imagen de los resultados de la simulación (véase la figura 1).

Figura 1: Resultado del banco de pruebas para el divisor.


Conclusión
Este divisor de frecuencia constituye una pequeña introducción práctica al diseño
de sistemas digitales con VHDL. En caso de necesitar ayuda con el diseño de
algún componente mediante VHDL, no dudes en comentar :).

S-ar putea să vă placă și