Este documento describe el diseño e implementación de un modulador PWM (modulación por ancho de pulso) con VHDL para controlar la intensidad de los LED en una placa de desarrollo. Se diseñaron tres subsistemas: un selector de frecuencia, un controlador de reloj y un generador PWM. El selector de frecuencia establece la frecuencia de operación basada en los botones de entrada. El controlador de reloj genera la señal de reloj modificada usando contadores. El generador PWM crea la señal PWM cuya duración de pulso
2. MARCO TEÓRICO
La modulación por ancho de pulsos PWM, consiste en la generación de pulsos que varían
en su ciclo útil de acuerdo con el valor de la señal de entrada.
El uso de PWM en el control ha sido ampliamente estudiado en los últimos años, y su
implementación tradicionalmente se ha basado en soluciones análogas y
microcontroladores.
Recientemente se han utilizado los DSP (procesadores digitales de señal) para la
modulación por ancho de pulso pero se ha encontrado la desventaja de la velocidad
máxima de operación en esta solución software.
Otro campo de estudio para el desarrollo de moduladores PWM, más recientemente
estudiado, ha sido la utilización de dispositivos de arreglo de campo programable FPGAs.
En la modulación por ancho de pulsos PWM, se transforma una señal análoga en una señal
digital con ancho de pulso proporcional al valor de la señal análoga previamente
muestreada.
Diseño estructural del PWM para VHDL
Entre las técnicas tradicionales para modular en PWM se encuentra el control todo/nada,
el cual requiere de un contador módulo N, de un comparador con una señal de referencia
y un registro.
El diseño estructural para el PWM sin control de frecuencia se muestra en la figura 1.
Figura 1.
Tratamiento de la señal en el PWM
En la Figura 2 se presenta una señal análoga con su correspondiente señal digital PWM, se
puede apreciar que a mayor valor análogo de entrada, mayor es el ciclo útil de la señal de
salida digital (mayor tiempo alto con respecto al periodo de la señal).
3. Figura 2.
Buffer FIFO o Buffer circular
Un buffer circular, buffer cíclico o buffer de anillo es una estructura de datos que utiliza un
buffer único o array ordinario y que adopta su nombre por la forma en que se escriben o
leen sus elementos.
El buffer circular es bien conocido como un buffer FIFO (primero en entrar es el primero
en salir).
Un buffer circular trabaja básicamente con dos punteros para acceder a los elementos del
buffer, que aquí llamaremos wr y rd.
Ambos índices tienen avance incremental y cíclico, es decir, se incrementan de uno en uno
y luego de apuntar al último elemento del buffer vuelven a apuntar al primero.
Al inicio los dos índices apuntan al primer elemento del buffer.
A continuación se explica cómo y cuándo se incrementan los punteros (ver figura 3):
- Cada nuevo dato a guardar en el buffer será depositado en la casilla
actualmente apuntada por wr. A continuación wr se incrementa en uno.
- Por otro lado, cada dato que salga del buffer será el de la casilla actualmente
apuntada por rd. A continuación rd se incremente en uno.
4. Figura 3. Modos de operación del Buffer Circular
Problemas en el diseño de Buffers circulares.
Un buffer circular puede presentar ciertos problemas que hay que saber controlar.
En concreto las situaciones problemáticas si no son controladas son en el caso de que
el buffer se encuentre totalmente vacío o totalmente lleno.
En estos casos el índice de escritura coincidirá con el de lectura, lo que provocará
fallos tales como que se lea información incorrecta del buffer o que se escriba
información destruyendo información útil del buffer, provocando que ésta no pueda
ser leída.
5. PLANTEAMIENTO DEL PROBLEMA
OBJETIVOS DE LA PRÁCTICA
Diseñar y codificar en VHDL un modulador por Ancho de Pulso (PWM) para el control del
ciclo de trabajo de un tren de pulsos de frecuencia variable.
Diseñar y codificar un buffer FIFO utilizando la técnica de control circular para el
direccionamiento de las operaciones lectura/escritura.
RESULTADOS ESPERADOS DE LA PRÁCTICA
Modular una señal periódica cuadrada utilizando la técnica PWM de frecuencia variable,
con control del ancho de pulso y reset asíncrono para controlar la intensidad de
iluminación de los leds proporcionados en el tablero Nexys-2.
Implementar un elemento de memoria temporal utilizando un buffer tipo FIFO que
permita almacenar n bits en 2m-1 direcciones.
7. PWM VARIABLE
Diseñar, codificar, simular y verificar un PWM para controlar la intensidad de los leds del
tablero de desarrollo con una granularidad del 1% en los cambios de intensidad y control
de frecuencia de la señal portadora.
La frecuencia de la señal portadora estará definida por los 4 pulsadores que integra el
tablero considerando los siguientes valores: 1 MHz, 100 KHz, 1 KHz, 100 Hz.
Figura 4. PWM
DESARROLLO DEL PWM
EL PWM DESARROLLADO TIENE LA SIGUIENTE ESTRUCTURA, QUE ES EL
PROGRAMA DE MAYOR JERARQUÍA.
Figura 5.
Las entradas están dentro del recuadro verde de la figura 5 las cuales son:
Inswitch[6:0]
o Básicamente estos son los switches del Nexys 2, solo se utilizaron 7 de los 8 con
los que cuenta la tarjeta.
o La función con la que cuenta es:
De acuerdo a los requerimientos de la práctica se tiene que variar la
intensidad de brillo de los leds por medio de los switches.
Figura 6.
8. Se variara el ciclo de trabajo de la señal de PWM con el uso de los
switches, ya que por medio de esto podremos controlar el voltaje RMS de
la señal y como consecuencia la corriente que pasara por los leds,
entonces de manera digital controlaremos el porcentaje del ciclo de
trabajo, lo cual se muestra en la tabla 1.
Tabla 1. Variación del ciclo de trabajo con respecto a la combinación de la entrada de switches.
Inswitch[6:0] Decimal % Ciclo de
trabajo
0000000 0 0
0110010 50 50
1100011 99 99
1100100 100 100
1100101 101 100
… … 100
1111111 127 100
Clk
o En las señal de reloj de 50 MHz de la tarjeta
Rst
o Con botón volveremos a cero todos los contadores en el subprograma
“CONTROL_DE_FRECUENCIA_RELOJ”.
Inpush[3:0]
o Estos botones de manera general serán utilizados para determinar cual será la
frecuencia de la señal PWM, tal y como se muestra en la tabla 2.
Tabla 2.
Inpush[3:0] Frecuencia
0000 No importa
0001 100 Hz
0010 1 KHz
… No importa
0100 100 KHz
… No importa
1000 1 MHz
… No importa
1111 No importa
Las salidas del programa de mayor jerarquía son las que están dentro del recuadro
azul, las cuales son:
Salida[6:0]
o Esta es básicamente la señal de PWM que se conecta a cada uno de los leds del 0
al 7 de la tarjeta Nexys 2.
9. LOS SUBPROGRAMAS REALIZADOS SE PUEDE OBSERVAR COMO BLOQUES EN LA FIGURA
5, Y SON:
SELECTOR_DE_FRECUENCIA (FREC_SEL.vhd)
Este programa cumple la función de habilitar los contadores en el programa
CONTROL_DE_FRECUENCIA (FREC_CTRL.vhd), y lo hace enviando una combinación, la cual
es generada a partir de la lectura de los push botton de la entrada.
Figura 7.
Las entradas son:
o Inpush[3:0]
Esta entrada generara una combinación la cual se asignara a la salida del mismo
programa, las combinaciones son las mostradas en la tabla 3.
Las salidas son:
o freq_sel[1:0]
Esta salida será ingresada como una señal de entrada al programa
CONTROL_DE_FRECUENCIA (FREC_CTRL.vhd).
Tabla 3. Asignación de valor de salida con respecto a la señal de entrada
Inpush[3:0] freq_sel[1:0]
0001 11
0010 10
0100 01
1000 00
El resto de combinaciones de la entrada no importan
CONTROL_DE_FRECUENCIA_RELOJ (FREC_CTRL.vhd)
Este programa cumple la función generar las diferentes frecuencias de la señal de
reloj modificada, la cual se ingresara en el programa GENERADOR_DE_PWM
(PWM_01.vhd), estas frecuencias son generadas utilizando contadores.
10. Figura 8.
Las entradas del programa son:
o clk
Es la señal de reloj de 50 MHz de la tarjeta.
o sel_freq[1:0]
Es la señal de salida (freq_sel [1:0]) del programa anterior, dependiendo de la
combinación de esta señal, se habilitara un determinado contador, que generar
una frecuencia de reloj determinada, en la tabla 4 se muestra la relación entra la
señal de entrada y los contadores que se habilitaran.
o reset
Vuelve a todos los contadores del programa a cero.
Las salidas del programa son:
o clk_mod
Es la nueva señal de reloj con frecuencia modificada que será ingresada con el
programa GENERADOR_DE_PWM (PWM_01.vhd).
11. GENERADOR_DE_PWM (PWM_01.vhd)
Este programa es el encargado de generar la señal de PWM.
Figura 9.
Las entradas del programa son:
o clk_var
A esta entrada se le asigna la señal de salida del programa
CONTROL_DE_FRECUENCIA (FREC_CTRL.vhd), esta es la señal de reloj utilizada
para generar la señal de PWM variable.
o entrada[6:0]
Con esta entrada se varía el porcentaje del ciclo de trabajo y por lo tanto se varía
como consecuencia la intensidad luminosa del led, el porcentaje se varía
dependiendo de la combinación, una combinación entre el 0000000 y 1100100
generan un cambio de 0 al 100 % el ciclo de trabajo respectivamente.
o reset
Este reset vuelve el contador que genera la señal de PWM a cero.
Las salidas son:
o salida[6:0]
De esta salida se obtiene la señal de PWM que se ingresa en los diferente leds.
12. LOS CODIGOS DE LOS PROGRAMAS REALIZADOS SON:
PWM_PRACTICA4.vhd (PROGRAMA DE MAYOR JERARQUIA)
Figura 10.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity PWM_PRACTICA4 is
PORT ( inswitch: in std_logic_vector(6 downto 0);
inpush: in std_logic_vector(3 downto 0);
rst, clk: in std_logic;
salida: out std_logic_vector(6 downto 0)
);
end PWM_PRACTICA4;
architecture Behavioral of PWM_PRACTICA4 is
signal freq_selin: std_logic_vector(1 downto 0);
signal clk_modin: std_logic;
begin
SELECTOR_DE_FRECUENCIA: entity work.FREC_SEL(Behavioral)
PORT MAP(inpush => inpush, freq_sel => freq_selin);
CONTROL_DE_FRECUENCIA_RELOJ: entity work.FREQ_CTRL(Behavioral)
PORT MAP(sel_freq => freq_selin, reset => rst, clk => clk, clk_mod => clk_modin);
GENERADOR_DE_PWM: entity work.PWM_01(Behavioral)
PORT MAP(entrada => inswitch, salida => salida, clk_var => clk_modin, reset => rst);
end Behavioral;
13. SELECTOR DE FRECUENCIA (FREC_SEL.vhd)
Figura 11.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity FREC_SEL is
PORT( inpush: in std_logic_vector(3 downto 0);
freq_sel: out std_logic_vector(1 downto 0)
);
end FREC_SEL;
architecture Behavioral of FREC_SEL is
signal inpush_entero: integer range 0 to 15;
signal inpush_unsigned: unsigned(3 downto 0);
begin
inpush_unsigned <= unsigned(inpush);
inpush_entero <= to_integer(inpush_unsigned);
process(inpush_entero,inpush)
begin
if(inpush_entero > 0) then
if(inpush="1000")then
freq_sel <= "00";
elsif(inpush="0100")then
freq_sel <= "01";
elsif(inpush="0010")then
freq_sel <= "10";
elsif(inpush="0001")then
freq_sel <= "11";
end if;
end if;
end process;
end Behavioral;
14. CONTROL_DE_FRECUENCIA_RELOJ (FREC_CTRL.vhd)
Figura 12.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity FREQ_CTRL is
Port ( clk : in STD_LOGIC;
sel_freq : in STD_LOGIC_VECTOR(1 downto 0);
clk_mod : out STD_LOGIC;
reset : in STD_LOGIC);
end FREQ_CTRL;
architecture Behavioral of FREQ_CTRL is
SIGNAL contador1 : integer range 0 to 24 := 0; --1 MHz
SIGNAL contador2 : integer range 0 to 249 := 0; --100 kHz
SIGNAL contador3 : integer range 0 to 24999:= 0; --1 kHz
SIGNAL contador4 : integer range 0 to 249999 := 0; --100 Hz
SIGNAL temp: std_logic;
begin
process(clk,sel_freq)
begin
if (reset = '1') then
temp <= '0';
contador1 <= 0;
contador2 <= 0;
contador3 <= 0;
contador4 <= 0;
elsif (clk'event and clk = '1') then
case sel_freq is
when "00" =>
if(contador1 = 24) then
15. contador1 <= 0;
temp <= not(temp);
else
contador1 <= contador1 + 1;
end if;
contador2 <= 0;
contador3 <= 0;
contador4 <= 0;
when "01" =>
if(contador2 = 249) then
contador2 <= 0;
temp <= not(temp);
else
contador2 <= contador2 + 1;
end if;
contador1 <= 0;
contador3 <= 0;
contador4 <= 0;
when "10" =>
if(contador3 = 24999) then
contador3 <= 0;
temp <= not(temp);
else
contador3 <= contador3 + 1;
end if;
contador1 <= 0;
contador2 <= 0;
contador4 <= 0;
when others =>
if(contador4 = 249999) then
contador4 <= 0;
temp <= not(temp);
else
contador4 <= contador4 + 1;
end if;
contador1 <= 0;
contador2 <= 0;
contador3 <= 0;
end case;
end if;
end process;
clk_mod <= temp;
end Behavioral;
16. GENERADOR_DE_PWM (PWM_01.vhd)
Figura 13.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
entity PWM_01 is
Port( clk_var,reset: in std_logic;
entrada: in std_logic_vector(6 downto 0);
salida: out std_logic_vector(6 downto 0)
);
end PWM_01;
architecture Behavioral of PWM_01 is
signal cnt: unsigned(6 downto 0);
begin
process(clk_var,reset,entrada)
begin
if(reset = '1')then
cnt <= (others => '0');
elsif(clk_var'event and clk_var = '1')then
if(cnt = 99)then
cnt <= (others => '0');
else
cnt <= cnt + 1;
end if;
end if;
end process;
-- Asignacion de Señales
salida <= "1111111" when (cnt<unsigned(entrada))
else "0000000";
end Behavioral;
17. CONDIGO REALIZADO PARA LA SIMULACION
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY PWM_PRACTICA4_TB IS
END PWM_PRACTICA4_TB;
ARCHITECTURE behavior OF PWM_PRACTICA4_TB IS
-- Component Declaration for the Unit Under Test (UUT)
COMPONENT PWM_PRACTICA4
PORT(
inswitch : IN std_logic_vector(6 downto 0);
inpush : IN std_logic_vector(3 downto 0);
rst : IN std_logic;
clk : IN std_logic;
salida : OUT std_logic_vector(6 downto 0)
);
END COMPONENT;
--Inputs
signal inswitch : std_logic_vector(6 downto 0) := (others => '0');
signal inpush : std_logic_vector(3 downto 0) := (others => '0');
signal rst : std_logic := '0';
signal clk : std_logic := '0';
--Outputs
signal salida : std_logic_vector(6 downto 0);
-- Clock period definitions
constant clk_period : time := 20 ns;
BEGIN
-- Instantiate the Unit Under Test (UUT)
uut: PWM_PRACTICA4 PORT MAP (
inswitch => inswitch,
inpush => inpush,
rst => rst,
clk => clk,
salida => salida
);
-- Clock process definitions
clk_process :process
begin
clk <= '0';
wait for clk_period/2;
clk <= '1';
wait for clk_period/2;
end process;
-- Stimulus process
stim_proc: process
begin
rst <= '1';
18. wait until falling_edge(clk);
wait until falling_edge(clk);
rst <= '0';
wait until falling_edge(clk);
wait until falling_edge(clk);
inswitch <= "0000100";
wait until falling_edge(clk);
wait until falling_edge(clk);
inpush <= "1000";
wait until falling_edge(clk);
wait until falling_edge(clk);
inpush <= "0000";
for i in 1 to 1000 loop --Run 10 clocks
wait until falling_edge(clk);
end loop;
assert false
report "Simulation Completed"
severity failure;
end process;
END;
Figura 14.
En la figura 14 se puede observar que en el intervalo de tiempo de 0 ns a 40 ns es reset esta
en alto, por lo tanto en la salida[6:0] se tendrá una salida en cero, por lo tanto los leds estarán
apagados, después entre el intervalo de 40 ns a 80 ns también se tiene la salida en cero, debido a
que el ciclo de trabajo de la señal PWM está en cero, el cual se establece por medio de la entrada
inswitch[6:0], el cual se encuentra en cero en este mismo intervalo.
El la figura 14 se pude ver que después de 80 ns el ciclo trabajo de la señal PWM está en un
7% del 100% ya que la entrada inswitch[6:0] es “0000100”, por lo tanto la salida [6:0] se tiene un
valor de “1111111” y este valor se mantiene entre el intervalo de 80 ns (figura 14) a 3610 ns
(figura 15) y después la salida[6:0] se mantiene en “0000000” desde el intervalo de 3610 ns (figura
15) a 99610 ns (Figura 16).
19. Figura 15.
Figura 16.
ESPECIFICACION DE LAS ENTRADAS Y SALIDAS DE LA TARJETA
Figura 17.
Inpush[3] inpush[2] inpush[1] inspush[0]rst inswitch[6] … inswitch[0]
salida[6] … salida[0]
20. LA ASIGNACIÓN DE PINES EN PlanAhead ES:
# PlanAhead Generated physical constraints
NET "inpush[3]" LOC = H13;
NET "inpush[2]" LOC = E18;
NET "inpush[1]" LOC = D18;
NET "inpush[0]" LOC = B18;
NET "inswitch[6]" LOC = N17;
NET "inswitch[5]" LOC = L13;
NET "inswitch[4]" LOC = L14;
NET "inswitch[3]" LOC = K17;
NET "inswitch[2]" LOC = K18;
NET "inswitch[1]" LOC = H18;
NET "inswitch[0]" LOC = G18;
NET "clk" LOC = B8;
NET "rst" LOC = R17;
NET "salida[0]" LOC = J14;
NET "salida[1]" LOC = J15;
NET "salida[2]" LOC = K15;
NET "salida[3]" LOC = K14;
NET "salida[4]" LOC = E17;
NET "salida[5]" LOC = P15;
NET "salida[6]" LOC = F4;
22. FIFO BUFFER
Diseñar, codificar, simular y verificar un buffer FIFO de 8 direcciones que permita
escribir y leer registros de 5 bits. El registro inmediato para lectura/borrado deberá
mostrarse en dos displays de 7 segmentos.
Figura 18. Diagrama Conceptual de un FIFO Buffer
DESARROLLO DEL FIFO BUFFER
EL PROGRAMA DE MAYOR JERARQUÍA TIENE LA ESTRUCTURA SIGUIENTE
Figura 19. Estructura general del FIFO BUFFER
Las entradas del programa están dentro del recuadro punteado de color rojo de la
figura 19, las cuales son:
Dato[4:0]
rd
wr
clk
Las salidas del programa se encuentran dentro del recuadro punteado de color azul
de la figura 19, y son:
an_out[3:0]
sseg_out[6:0]
empty_out
full_out
23. Las funciones que cumplen las entradas serán explicadas a continuación, ya que estos
realizan una función determinada en cada uno de los subprogramas creados.
LOS SUBPROGRAMAS CREADOS SE PUEDEN VER COMO BLOQUES EN LA FIGURA
19, LAS CUALES SE EXPLICARAN A CONTINUACION
DEBOUNCE(DEBOUNCE.vhd)
Debido a que se usaron push botton para la escritura y la lectura de los datos
almacenados en la memoria, surgieron errores al momento del diseño, ya que los
pulsadores al ser presionados generan varios pulsos (ver figura 21) y este programa
se utilizo para evitar este problema.
Figura 20.
Figura 21. Cronograma de la salida del detector de flancos (PULSO_BTN) cuando hay
rebotes en la entrada(BTN)
La eliminación de rebotes se hará a través de un circuito diseñado especialmente para
esto y que se llevará a cabo en una prensa de botón y en un comunicado. La idea básica es
la siguiente: hay que detectar si el botón ha sido presionado.
Si es así, entonces el circuito de espera 5 milisegundos para que la señal este limpio.
Después de los 5 ms, si todavía se presiona el botón tendremos una operación de prensa
válida. Ahora tenemos que esperar a que el botón sea liberado. Cuando esto sucede, El
rebote se filtra por esperar otros 5 ms y los controles de circuito de nuevo si todavía se
suelta el botón. Si es así, la operación de liberación es válida. El diagrama de flujo siguiente
se describe este proceso:
24. Figura 22.
Este diagrama de flujo se traduce en VHDL de la siguiente manera: toda la supresión
de rebotes se hará en un proceso que se sincroniza con el oscilador de la junta. El contador
está representado por una variable de 20 bits que cuenta
0-999.999 asegurando la división del de 50 MHz a 50 Hz o 5 ms. Cada vez que entra
en el proceso vamos a comprobar para ver si la entrada (el botón) es el mismo que su
valor de edad. Si no, entonces el contador se restablece a 0 y el valor antiguo del botón se
actualiza. Si la entrada es del mismo valor que el valor antiguo entonces el contador se
incrementa a continuación, un cheque de su valor está hecho. Si el contador llega a 999
999 y la entrada tiene el mismo valor que el anterior entonces el botón obtiene ese valor.
De esta manera nos aseguramos de que la eliminación de rebotes sucede en una
prensa y una liberación y cualquier operación de prensa / liberación no válidos se filtra.
Las entradas de este subprograma son:
o bi
Para el caso de la entrada rd del programa de mayor jerarquía
FIFO_BUFFER.vhd
A entrada bi del subprograma DEBOUNCE.vhd se le asigna el valor de la
entrada rd de mayor jerarquía FIFO_BUFFER.vhd, este es el push botón
H13 de la tarjeta.
Para el caso de la entrada wr del programa de mayor jerarquía
FIFO_BUFFER.vhd
A entrada bi del subprograma DEBOUNCE.vhd se le asigna el valor de la
entrada wr de mayor jerarquía FIFO_BUFFER.vhd, este es el push botón E8
de la tarjeta.
o Clk
Es el reloj de 50 MHz de la tarjeta Nexys 2.
25. DETECTOR_FLANCO(DETEC_FLANCO_SUBIDA.vhd)
Este se encarga de generar un pulso en la salida cuando se detecta un flanco
ascendente en la entrada.
Figura 23. DETEC_FLANCO_SUBIDA.vhd
En la figura 24 se muestra el cronograma de lo que sucede al pulsar el botón. Como la
frecuencia del reloj es muy alta respecto a la velocidad humana, por muy rápido que
pulsemos y soltemos el pulsador, el tiempo de pulsación durará muchos ciclos de reloj. Y
por tanto, el estado del biestable (Q) cambiará en todos los ciclos de reloj mientras el
pulsador está presionado. Al soltar el pulsador, el estado final será aleatorio, según se
haya mantenido pulsado durante un número par o impar de ciclos.
Figura 24. Cronograma resultante de pulsar el botón de encendido/apagado
Para evitar este efecto, lo que tenemos que hacer es un circuito detector de flancos,
que transformará nuestro pulso humano en pulso del periodo del reloj. Podríamos pensar
en utilizar la expresión T'event and T='1' para detectar el flanco de subida. Y
seguramente funcione, sin embargo hemos visto que sólo debemos utilizar dicha
expresión para la señal de reloj. Porque si no, estaremos usando la señal T como reloj del
biestable, y en diseños síncronos no es recomendable. Así que tenemos que pensar en
otra alternativa.
El detector de flanco se puede realizar con una máquina de estados que detecte la
secuencia 0‐1 para flancos de subida y la secuencia 1‐0 para flancos de bajada.
La máquina de estados podemos hacerla mediante diagrama de estados o bien
mediante registros de desplazamiento (usando biestables D). Por ser una secuencia
pequeña, la haremos mediante registros de desplazamiento. No usaremos directamente la
entrada debido a que el pulsador es una entrada asíncrona, sino que la registraremos (la
pasaremos por un biestable) para evitar metaestabilidad y para evitar que el pulso
resultante dure muy poco. El circuito detector de flanco sin registrar la entrada se muestra
en la figura 5.5 (realizado con registro de desplazamiento). Es una máquina de Mealy
porque la salida depende de la entrada (BTN0).
26. Figura 25. Circuito detector de flanco sin registrar la entrada (Mealy: no recomendado para este
caso)
Sin embargo, el circuito de la figura 25 no es aconsejable porque la entrada BTN0 es
asíncrona, pues el pulsador puede ser presionado en cualquier momento. Esto puede
ocasionar metaestabilidad o anchos de pulsos muy pequeño. El cronograma de este
circuito para dos pulsaciones se muestra en la figura 26. En ella se puede ver que el
segundo pulso está muy cerca del flanco del reloj, esto produce una salida muy estrecha, y
además podría provocar metaestabilidad.
Figura 26. Cronograma del detector del flanco realizado por Mealy (no recomendado para este
caso)
Para evitar estos inconvenientes se hace el registro de desplazamiento con la entrada
registrada (máquina de Moore). La figura 27 muestra la versión Moore del circuito de
detector de flanco realizada con registros de desplazamiento.
Figura 27. Circuito detector de flanco registrando la entrada (Moore: recomendado para este
caso)
Ahora los pulsos resultantes siempre durarán un ciclo de reloj, como puedes ver en el
cronograma de la figura 28.
Figura 28. Cronograma del detector de flanco realizado por Moore (Recomendado para este caso)
27. Las entradas del detector de flancos son:
o BTN0
Se encarga de introducir el flanco ascendente de entrada.
o clk
Es el reloj de 50 MHz de la tarjeta Nexys 2.
Las salidas del detector de flancos son:
o PULSO_BTN0
Es el pulso de salida, el cual sale del filtro por el que la señal de entrada es
procesada para generar un solo pulso.
MEMORIA (FIFO_PRUEBA.vhd)
Un FIFO (First In First Out) buffer es una memoria elástica generalmente utilizado entre
dos subsistemas. Como su nombre lo indica la memoria que se escribe primero en la FIFO
es el primero en ser leídos o procesados. Un FIFO tiene dos señales de control, es decir,
escribir y leer. Cuando se escriben datos habilitada se escribe en el búfer y cuando se lee
está habilitada datos se "retira" de la memoria intermedia para hacer espacio para más
datos. Este concepto de escribir y leer (quitar) puede entenderse mejor en el diagrama
conceptual de un FIFO de la figura 18.
Como puede verse, una vez que se leen los datos, puede ser considerado como retirados y
permitiendo así más datos para ser escritos en el buffer.
Para implementar FIFO nos tenemos que imaginar los componentes de memoria, para ser
dispuestos en forma de cola circular con dos punteros, escribir y leer. Los escritura puntero
apunta al inicio del círculo mientras que las de lectura puntero apunta a la final del círculo.
Ambos indicadores se incrementan a sí mismos por uno después de cada operación de
lectura o escritura. Este tampón también constará de dos banderas; vacíos y llenos. Estos
le ayudan en la que indica cuando el FIFO está lleno (no se puede escribir a) o vacío (no se
puede leer desde). Esta implementación circular puede ser vista desde la figura 3:
Figura 29. FIFO_BUFFER
28. Las entradas del programa son:
o clk
Es el reloj de 50 MHz de la tarjeta Nexys 2.
o dato[4:0]
Con esta entrada podemos ingresar números desde el “00000” al “11111”(Digital)
o en decimal, desde el 0 al 31.
o rd
Con este botón habilitamos la lectura del valor en el que el puntero de rd se
encuentra.
o wr
Con este botón habilitamos la escritura del valor de la entrada en el espacio donde
se encuentra el puntero wr.
Las salidas del programa son:
o empty
Esta salida se encarga de informarnos por medio de un led si la memoria esta
vacía, cuando esta vacía la memoria un led se enciende y el led se apaga cuando
no esta vacía la memoria.
o full
Esta salida se encarga de encender un led cuando la memoria está llena y cuando
no lo está, este mismo led se apaga.
o salida[5:0]
Es el valor leído de la memoria.
CONVER_DECIMAL_A_DIGITOS (logicvect_toint_tohex.vhd)
Este transforma la señal lógica (resultado de las operaciones aritméticas) a numero
entero, el cual después se separa en dos dígitos y posteriormente se les aplica la
conversión inversa para que de esta manera se obtengan dos señales hexadecimales.
Figura 30.
Sus entradas son:
o Logicvect[5:0]
Entrada lógica de 6 bits (Resultado de las operaciones aritméticas realizadas en el
codificador de operaciones).
Sus salidas son:
o to_hex[3:0] y to_hex2[3:0]
Dos salidas hexadecimales que representan los dos dígitos correspondientes a la
decena y unidad del resultado obtenido del codificador de operaciones.
Para separa el numero en decena y unidad, lo que se hace es determinar
entre que rango se encuentra de valores se encuentra, por ejemplo:
29. Si la entrada logicvect es igual a 31(entero) o 11111(binario), entonces se
encuentra en el rango de de 30 a 39, entonces, sabemos que tiene como
decena el numero 3, entonces sabiendo esto, le restamos 30 al número de
entrada y el residuo será la unidad que en este caso es 1 (cero) y de esta
manera podremos obtener dos salidas hexadecimales, una para la decena
y otra para los unidades, los cuales se conectaran a un convertidos
hexadecimal a siete segmentos (“CONVER_HEX_A_SEG
(PRACTICA_02.vhd)”).
CONVER_HEX_A_SEG (PRACTICA_02.vhd)
Dos de estos son usados para representar el resultado de las operaciones aritméticas y
uno de estos utilizado para mostrar el código de la operación realizada con las entradas A
y B.
a) b)
Figura 31.
Sus entradas son:
o Hex[3:0]: Una entrada hexadecimal de 4 bits.
Sus salidas son:
o Sseg[6:0]: Una salida de 7 bits, los cuales representaran cada uno de los
segmentos de los displays.
MULTIPLEXOR_DISPLAYS (mux_3displays_time)
Figura 32.
Sus entradas son:
D0[6:0], D1[6:0]: Dos entradas de 7 bits, los cuales representan la decena y la unidad del
resultado obtenido del codificador de operaciones (instancia o programa
“CONVER_DECIMAL_A_DIGITOS (logicvect_toint_tohex.vhd)”).
30. o clk: Una señal de reloj, utilizado para multiplexar un display a la vez en cada flanco
de subida.
o rst1: utilizado para borrar todo de los displays.
Sus salidas son:
o sseg1[6:0]: Una salida de 7 bits, los cuales activaran los segmentos de los displays,
dependiendo de la entrada que se le m
o an[2:0]: Una salida de 3 bits utilizada para activar el display correspondiente con
cada digito (Código y resultado de la operación en forma de números enteros).
El multiplexado se realizara utilizando un contador de 1 bits, los bits 0 y 1 activaran D0, D1,
además de que también determinara que entrada asignarle a la salida. El contador será
controlado por medio de una señal de reloj, este contador aumentara una unidad cada
flanco de subida.
31. LOS CODIGOS DE LOS PROGRAMAS REALIZADOS SON:
FIFO_BUFFER.vhd (Programa de Mayor Jerarquia)
Figura 33.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity FIFO_BUFFER is
PORT( rd, wr, clk: in STD_LOGIC;
dato: in STD_LOGIC_VECTOR(4 downto 0);
sseg_out: out STD_LOGIC_VECTOR(6 downto 0);
an_out: out STD_LOGIC_VECTOR(3 downto 0);
full_out,empty_out: out std_logic
);
end FIFO_BUFFER;
architecture Behavioral of FIFO_BUFFER is
signal salidain: std_logic_vector(5 downto 0);
signal to_hex1in,to_hex2in: std_logic_vector(3 downto 0);
signal disp0, disp1: std_logic_vector(6 downto 0);
signal pulsoin_wr, pulsoin_rd: std_logic;
signal bo_in,bo_in1: std_logic;
begin
DEBOUNCE_WR: entity work.DEBOUNCE(Behavioral)
PORT MAP(clk => clk,bi => wr, bo => bo_in);
DEBOUNCE_RD: entity work.DEBOUNCE(Behavioral)
PORT MAP(clk => clk,bi => rd, bo => bo_in1);
DETECTOR_FLANCO_WR: entity work.DETEC_FLANCO_SUBIDA(Behavioral)
PORT MAP(BTN0 => bo_in, clk => clk, PULSO_BTN0 => pulsoin_wr);
DETECTOR_FLANCO_RD: entity work.DETEC_FLANCO_SUBIDA(Behavioral)
PORT MAP(BTN0 => bo_in1, clk => clk, PULSO_BTN0 => pulsoin_rd);
MEMORIA: entity work.FIFO_PRUEBA(Behavioral)
PORT MAP(rd => pulsoin_rd, wr => pulsoin_wr, clk => clk, dato => dato, salida => salidain,full =>
full_out, empty => empty_out);
32. CONVER_DECIMAL_A_DIGITOS: entity work.logicvect_toint_tohex(Behavioral)
PORT MAP(logicvect => salidain,to_hex1 => to_hex1in,to_hex2 => to_hex2in);
CONVER_HEX_A_SEG1: entity work.PRACTICA2_02(Behavioral)
PORT MAP(hex => to_hex1in,sseg => disp0);
CONVER_HEX_A_SEG2: entity work.PRACTICA2_02(Behavioral)
PORT MAP(hex => to_hex2in,sseg => disp1);
MULTIPLEXOR_DISPLAYS: entity work.mux_3display_time(Behavioral)
PORT MAP(D0 => disp0, D1 => disp1, clk => clk, sseg1 => sseg_out,an => an_out);
end Behavioral;
33. DEBOUNCE.vhd
a) b)
Figura 34.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity DEBOUNCE is
PORT( clk: in std_logic;
bi: in std_logic;
bo: out std_logic
);
end DEBOUNCE;
architecture Behavioral of DEBOUNCE is
signal button: std_logic:='0';
begin
process(clk,button)
variable oldb: std_logic:='1';
variable cnt: std_logic_vector(19 downto 0);
begin
if(clk'event and clk = '1')then
if((bi xor oldb)='1')then
cnt:=(others => '0');
oldb:=bi;
else
cnt:=cnt+'1';
if((cnt=x"F423F")and((oldb xor bi)='0'))then
button <= oldb;
end if;
end if;
end if;
bo<=button;
end process;
end Behavioral;
34. DETECTOR DE FLANCO (DETECTOR_FLANCO_SUBIDA.vhd)
a) b)
Figura 35.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity DETEC_FLANCO_SUBIDA is
PORT( BTN0, clk: in std_logic;
PULSO_BTN0: out std_logic
);
end DETEC_FLANCO_SUBIDA;
architecture Behavioral of DETEC_FLANCO_SUBIDA is
signal BTN0_RG1, BTN0_RG2: std_logic;
--signal Q_T: std_logic;
--signal cont: unsigned(2 downto 0):="000";
begin
-----------BIESTABLE D1
process(clk)
begin
if(clk'event and clk = '1')then
BTN0_RG1 <= BTN0;
end if;
end process;
-----------BIESTABLE D2
process(clk)
begin
if(clk'event and clk = '1')then
BTN0_RG2 <= BTN0_RG1;
end if;
end process;
PULSO_BTN0 <= '1' when (BTN0_RG1 = '1' and BTN0_RG2 = '0')
else '0';
end Behavioral;
35. MEMORIA (FIFO_PRUEBA.vhd)
Figura 36.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity FIFO_PRUEBA is
GENERIC( adress:natural:=8;
bits:natural:=5
);
PORT( rd, wr, clk: in std_logic;
dato: in std_logic_vector(4 downto 0);
salida: out std_logic_vector(5 downto 0);
full: out std_logic;
empty: out std_logic
);
end FIFO_PRUEBA;
architecture Behavioral of FIFO_PRUEBA is
CONSTANT N1:integer:=22;
signal cont_rd,cont_wr: unsigned (2 downto 0):="000";
signal cont_rd_int,cont_wr_int: integer range 0 to 7:=0;
type matriz is array (2**adress-1 downto 0)
of std_logic_vector(bits-1 downto 0);
signal registro: unsigned(adress-1 downto 0):="00000000";
signal registrologic: std_logic_vector(adress-1 downto 0);
signal memoria: matriz;
begin
process(clk,rd,wr)
begin
if(clk'event and clk='1')then
if(wr = '1')then
if(registro(cont_wr_int)= '0')then
memoria(cont_wr_int) <= dato;
registro(cont_wr_int) <= '1';
cont_wr <= cont_wr + 1;
end if;
36. end if;
cont_wr_int <= to_integer(cont_wr);
if(rd = '1')then
if(registro(cont_rd_int) = '1')then
registro(cont_rd_int)<='0';
salida <= memoria(cont_rd_int);
cont_rd <= cont_rd + 1;
end if;
end if;
cont_rd_int <= to_integer(cont_rd);
end if;
if(registro = "00000000")then
full <= '0';
empty <= '1';
elsif(registro = "11111111")then
full <= '1';
empty <= '0';
else
full <= '0';
empty <= '0';
end if;
end process;
end Behavioral;
37. CONVER_DECIMAL_A_DIGITOS (logic_toint_tohex.vhd)
Figura 37.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity logicvect_toint_tohex is
PORT( logicvect: in std_logic_vector(5 downto 0);
to_hex1,to_hex2: out std_logic_vector(3 downto 0)
);
end logicvect_toint_tohex;
architecture Behavioral of logicvect_toint_tohex is
signal logicvect_entero: integer range 0 to 70;
signal resta: integer range 0 to 15;
signal logicvect_unsigned: unsigned(5 downto 0);
begin
logicvect_unsigned <= unsigned(logicvect);
logicvect_entero <= to_integer(logicvect_unsigned);
process(logicvect_entero)
begin
--if(in_signo = '0') then
if(logicvect_entero = 70) then
resta <= logicvect_entero - 70;
to_hex1 <= std_logic_vector(to_unsigned(resta,4));--Calculando
UNIDAD
to_hex2 <= "0111"; --La DECENA es una 7 por que entro en este
rango
elsif(logicvect_entero >= 60 and logicvect_entero <= 69) then
if(logicvect_entero = 63)then
resta <= 0;
to_hex1 <= "1111"; --El E
to_hex2 <= "1111"; --EL E
else
resta <= logicvect_entero - 60;
to_hex1 <= std_logic_vector(to_unsigned(resta,4));--
Calculando UNIDAD
to_hex2 <= "0110"; --La DECENA es un 6 por entrar en
este rango
end if;
elsif(logicvect_entero >= 50 and logicvect_entero <= 59) then
resta <= logicvect_entero - 50;
38. to_hex1 <= std_logic_vector(to_unsigned(resta,4));--Calculando
UNIDAD
to_hex2 <= "0101"; --La DECENA es un 5
elsif(logicvect_entero >= 40 and logicvect_entero <= 49) then
to_hex1 <= "1111";
to_hex2 <= "1111"; --La decena es un 4
elsif(logicvect_entero >=30 and logicvect_entero <= 39) then
resta <= logicvect_entero - 30;
to_hex1 <= std_logic_vector(to_unsigned(resta,4));--Calculando
UNIDAD
to_hex2 <= "0011"; --La decena es un 3
elsif(logicvect_entero >=20 and logicvect_entero <= 29) then
resta <= logicvect_entero - 20;
to_hex1 <= std_logic_vector(to_unsigned(resta,4));--Calculando
UNIDAD
to_hex2 <= "0010"; --La decena es un 2
elsif(logicvect_entero >=10 and logicvect_entero <= 19) then
resta <= logicvect_entero - 10;
to_hex1 <= std_logic_vector(to_unsigned(resta,4));--Calculando
UNIDAD
to_hex2 <= "0001"; --La decena es un 1
else
resta <= 0;
to_hex1 <=
std_logic_vector(to_unsigned((logicvect_entero+resta),4));
to_hex2 <= "0000"; --La decena es un 0
end if;
end process;
CONVERT_HEX_A_SEG( PRACTICA_02.vhd)
39. a) b)
Figura 38.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity PRACTICA2_02 is
port( hex: in std_logic_vector(3 downto 0);
sseg: out std_logic_vector(6 downto 0) --EXAMEN(6 downto 0) --PRACTICA2(7
downto 0);
);
end PRACTICA2_02;
architecture Behavioral of PRACTICA2_02 is
begin
process(hex)
begin
case hex is
when "0000" =>
sseg(6 downto 0) <= "0000001"; --0
when "0001" =>
sseg(6 downto 0) <= "1001111"; --1
when "0010" =>
sseg(6 downto 0) <= "0010010"; --2
when "0011" =>
sseg(6 downto 0) <= "0000110"; --3
when "0100" =>
sseg(6 downto 0) <= "1001100"; --4
when "0101" =>
sseg(6 downto 0) <= "0100100"; --5
when "0110" =>
sseg(6 downto 0) <= "0100000"; --6
when "0111" =>
sseg(6 downto 0) <= "0001111"; --7
when "1000" =>
sseg(6 downto 0) <= "0000000"; --8
when "1001" =>
sseg(6 downto 0) <= "0000100"; --9
when "1010" =>
sseg(6 downto 0) <= "0001000"; --A
when "1011" =>
sseg(6 downto 0) <= "1100000"; --b
when "1100" =>
40. sseg(6 downto 0) <= "0110001"; --C
when "1101" =>
sseg(6 downto 0) <= "1000010"; --d
when "1110" =>
sseg(6 downto 0) <= "0110000"; --E
when others =>
sseg(6 downto 0) <= "0111000"; --F --Modificado para ser signo Negativo
end case;
end process;
end Behavioral;
MULTIPLEXOR_DISPLAYS (mux_3display_time)
41. Figura 39.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity mux_3display_time is
PORT( D0, D1: in STD_LOGIC_VECTOR(6 downto 0);
clk: in STD_LOGIC;
sseg1: out STD_LOGIC_VECTOR(6 downto 0);
an: out STD_LOGIC_VECTOR(3 downto 0)
);
end mux_3display_time;
architecture Behavioral of mux_3display_time is
CONSTANT N1:integer:=18;
signal count1,count: unsigned(N1-1 downto 0);
signal sel: std_logic;
signal s1: std_logic_vector(3 downto 0);
begin
process(clk)
begin
if(clk'event and clk = '1') then --Agregado PRACTICA4
count1 <= count;
an <= s1;
end if;
end process;
count <= count1 + 1;
sel <= STD_LOGIC(count1(N1-1));
process(D0,D1,sel)--Agregado PRACTICA4
begin
case sel is
when '0' =>
sseg1 <= D0;
s1 <= "1110";
when others =>
sseg1 <= D1;
s1 <= "1101";
end case;
end process;
end Behavioral;
ESPECIFICACION DE LAS ENTRADAS Y SALIDAS DE LA TARJETA
42. Figura 40.
EVIDENCIA DEL FUNCIONAMIENTO
Figura 41. Estado inicial, el LD0 está encendido por que la memoria esta vacía
Figura 42. Memoria con datos, ningún led esta encendido, porque la memoria no esta vacía pero tampoco
vacía
Dato[4] Dato[3] Dato[2] Dato[1] Dato[0]
rd wr
Full_out empty_out
an_out[3] an_out[2] an_out[1] an_out[0]
sseg_ou[6] CA<L18>
…
sseg_out[0] CG<H14>
44. LA ASIGNACION DE PNES EN PlanAhead ES:
# PlanAhead Generated physical constraints
NET "dato[4]" LOC = L14;
NET "dato[3]" LOC = K17;
NET "dato[2]" LOC = K18;
NET "dato[1]" LOC = H18;
NET "dato[0]" LOC = G18;
NET "sseg_out[6]" LOC = L18;
NET "sseg_out[5]" LOC = F18;
NET "sseg_out[4]" LOC = D17;
NET "sseg_out[3]" LOC = D16;
NET "sseg_out[2]" LOC = G14;
NET "sseg_out[1]" LOC = J17;
NET "sseg_out[0]" LOC = H14;
NET "an_out[3]" LOC = F15;
NET "an_out[2]" LOC = C18;
NET "an_out[1]" LOC = H17;
NET "an_out[0]" LOC = F17;
NET "clk" LOC = B8;
NET "rd" LOC = H13;
NET "wr" LOC = E18;
NET "full_out" LOC = J15;
NET "empty_out" LOC = J14;