Esta es la última entrada de un tutorial que comprende las
últimas 5 entradas, este último tutorial (que empieza por lo más básico pues pretendo
que los lectores puedan empezar con pocos conocimientos), a su vez se basa en
otro tutorial de 12 entradas más enfocado en un tipo de hardware que yo uso (el
kit Open3S250E con una FPGA Spartan-3E).
El blog apenas tiene algo más de 2 semanas, es verdad que
voy a rachas, pero no se puede decir que no me esté tomando el blog en serio
¿no?.
Antes de nada recordemos que hace exactamente el sistema del
que estamos analizando su código VHDL, como este proyecto ya lo probamos en el
hardware y dicen que una imagen vale más que mil palabras (y si es en
movimiento mejor), dejo el siguiente clip:
Lo que
tenemos básicamente un sistema como este:
Es decir
una señal de reloj clk -que es la
del reloj del Core3S250E- actuando como entrada,
y 4 salidas a los LEDs L4, L3, L2, L1 que están en la
misma placa Core3S250E.
Y así
sucesivamente…
La señal
de reloj se usará para ir temporizando el tiempo en que aparece encendido un
LED hasta que se apaga y se enciende el siguiente.
Pero
este es el momento de coger o manejar la calculadora o de usar el lápiz y el
papel.
Si vamos
de nuevo a la página que se abre en el siguiente enlace:
En el
apartado que pone “What’s on Core3S250E” hay unas figuras dónde se numera los componentes y
una lista descriptiva de estos componentes, uno de esos componentes (que está
en la parte inferior de la placa) es el “50M Active Crystal Oscilator” numerado
como 12. Ese es reloj usado y la frecuencia con la que trabaja es de 50 MHz. Es
decir la frecuencia es:
fclk = 50 MHz = 50 x 106
Por otro
lado sabemos que el periodo es la inversa de la frecuencia y que los hercios
son la inversa de los segundos, 1 Hz = 1 sg-1 (y si no lo sabías o
no te acordabas te lo digo yo).
Tclk = 1/fclk
--------------------------------------------------------------------------------------------------------------------------
Nota: la anterior imagen la he tomado
de Internet y luego le he incluido cosas. Voy a dejar el link no sólo porque es
lo legal, también porque me ha parecido una página muy bien hecha y explicada:
--------------------------------------------------------------------------------------------------------------------------
Si
hacemos los cálculos tenemos que Tclk = 0,02 µs = 20 ns, es decir,
¡20 x 10-9 segundos!, ¡eso es mucho más rápido que el pestañeo de
una persona!.
Ese
tiempo es tan imperceptible para el ojo humano que si hiciéramos uso de esos
periodos de tiempo para las transiciones LED encendido => LED apagado =>
LED siguiente encendido, lo que veríamos seguramente es todos los LEDs
encendidos a la vez, o quién sabe si todos apagados, yo desde luego no voy a
hacer es prueba y someter los LEDs a esas velocidades de trabajo.
Es por
eso que lo que se hace en este programa es convertir la señal de reloj original
clk a otra de mayor período llamada clk1 (esto se hace en el proceso P1 dentro
de la arquitectura), y a su vez clk1 en otra de aun mayor período llamada clk2
(en el proceso P3). Las transiciones de encendido-apagado-encendido se harán
con la señal clk2 que si trabaja con periodos más perceptibles para las
personas.
Es decir,
en este programa se hace las siguientes transiciones de unas señales de
sincronización a otras:
Clk (reloj del Core3S250E) =>
clk1 => clk2
Con Tclk2
(periodo de la señal de sincronización con la que trabajamos) mucho mayor que Tclk (periodo
original del reloj del Core3S250E).
Recordemos
que la arquitectura (que es dónde realmente se implementa el algoritmo), la
estructuramos de la siguiente manera:
Empecemos
por el principio (como se suele empezar vaya):
No es de
extrañar que las señales clk1 y clk2 se declaren dentro de la arquitectura
(ergo no en la entidad), puesto que realmente no son entradas ni salidas del
sistema, son parámetros que usamos dentro de “la caja negra” para temporizar
las salidas y que los LEDs se enciendan y apaguen y lo podamos ver.
De nuevo
vemos que se declaran como tipo std_logic (con el color que no se nombrar ¿fucsia
quizás?), igual que la señal clk de entrada del reloj, como no podía ser de otra
forma.
Una vez
declarada la arquitectura, para que se empiece a ejecutar el algoritmo que
contiene siempre se usa la palabra reservada begin.
Esto
veremos que es también extensible a los procesos dentro de la arquitectura,
también ahí se usa esta instrucción: begin.
Las
señales LCD_N y LCD_P ya dijimos que son
prescindibles (de hecho ya no vuelven a usar en el programa), a mí se me
ocurren varias funcionalidades pero no es cuestión de aburrir con mis
conjeturas al lector (lector sin género definido por mí).
Y ya nos
viene lo más divertido, el primer proceso o process en inglés:
La lista
sensible de este proceso es clk, esto significa que este proceso se pondrá en
marcha si y sólo hay algún cambio en la señal de reloj.
De
manera que para que clk se modifique tiene que haber en ese instante un flanco
de subida o de bajada (ya adelanto que sólo se trabajará con flancos de
subida).
A continuación
se declara una variable con el sospechoso nombre de count, ¿será una
abreviación de counter (contador en inglés)?, veremos que la sospecha no es
infundada y que efectivamente hará de contador.
VARIABLE count: INTEGER RANGE 0 TO 9999999;
La
variable se declara de tipo entero entre los valores 0 y 9999999 (incluidos),
esto implica que count se irá
incrementando (veremos que de uno en uno) hasta llegar a 10.000.000, y justo en
esa cifra se reiniciará. Dicho de otra
forma, el contador se incrementará 10.000.000 veces antes de reiniciarse a
cero.
No es
extraño que la única variable que hasta ahora hemos encontrado se declare
dentro de un proceso, porque es dentro de los fragmentos de código que se
ejecutan secuencialmente dónde más sentido tiene las variables.
Tras
esto se ejecuta el begin de turno y aparece una instrucción que merece ser
explicada con un poco de más de profundidad.
Lo que
seguramente extrañe más a los que no estén familiarizados con el VHDL es ese
clk’EVENT con el apostrofe en medio sorprendiendo.
clk’EVENT
significa que se ha producido un cambio en la señal clk, este puede ser un
flanco de subida o de bajada. A el “’EVENT” que añadimos justo a continuación
de la señal (en este caso clk), es lo que se llama en VHDL un atributo, y en
este caso devolverá el valor 1 (true o verdadero) cuando haya un cambio en la
señal clk, y 0 (false o falso) cuando no haya un cambio en la señal clk.
Para que
lo entienda mejor el que no esté familiarizado con la programación lo explicaré
como sigue:
La
sentencia condicional (o también conocida como de bifurcación),
IF clk'EVENT
está
esperando que haya un cambio en clk, por tanto en cada momento habrá dos opciones:
---------------------------------------------------------------------------------
Opción 1:
SI hay un cambio en clk (de 0 a 1,
o de 1 a 0) => clk'EVENT = 1 lógico
(1 lógico corresponde a verdadero):
IF
clk'EVENT THEN
--
Las instrucciones que hay en medio SI
se ejecutan.
END
IF;
---------------------------------------------------------------------------------
Opción 2:
NO hay un cambio en clk (clk está
en 0 o en 1 lógico de forma constante, sin que haya cambio) => clk'EVENT = 0 lógico (0 lógico corresponde a falso):
IF
clk'EVENT THEN
--
Las instrucciones que hay en medio NO
se ejecutan.
END
IF;
---------------------------------------------------------------------------------
Sin
embargo aquí no tenemos sólo IF clk’EVENT, tenemos:
IF clk'EVENT AND
clk='1' THEN …
Esto
quiere decir que para que se ejecuten las instrucciones entre el THEN y el END
IF, no basta con que haya un cambio de clk, además (Y, o en inglés AND) este
cambio tiene que dejar clk con valor 1, o sea, que el cambio debe de ser de 0 a
1.
Decir lo
que acaba de leer es lo mismo que decir que
“IF clk'EVENT AND
clk='1' THEN”
está esperando a que haya un flanco de subida para que se ejecute el código que viene a
continuación del THEN.
Si no,
se saltará todas esas instrucciones (las que están tras el THEN) hasta
encontrar una instrucción de tipo “ELSE” o “ELSIF”, o simplemente se continuará el programa con lo que venga
detrás del “END IF”.
Entre
las instrucciones que se encuentran entre el IF-THEN y el END IF;, pueden haber
otros IF-THEN…END IF, también con sus ELSE Y ELSIF. Esto se conoce como
sentencias de selección anidadas (unas dentro de otras) y en el caso que nos
ocupa de hecho las hay.
Cuando
recurra al diagrama de flujo esto se verá más claro.
El trozo
de código:
Se puede
representar como sigue:
Si nos
fijamos bien, cada flanco de subida inicia un periodo que acaba cuando vuelve a
haber otro flanco de subida, es por eso que cada vez que contamos un flanco de
salida estamos también contando periodos:
Teniendo
en cuenta la reflexión anterior, podemos entender lo que realmente hace el
proceso P1 para los cual usaremos el siguiente cronograma dónde se compara el
comportamiento de las señales clk y clk1.
De la
anterior figura se deduce que:
Tclk1 = (5000000+5000000) x Tclk
= 10.000.000 x Tclk = 10 x 106 x Tclk
Por otro
lado recordemos que obtuvimos Tclk:
Tclk = 20 ns = 20 x 10-9 s
= 20 x 10-3 x 10-6 segundos
Por
tanto:
Tclk1
= 20 x 10 x 10-3 s = 200 x 10-3
= 200 ms = 0,2 segundos
Con esto
ya empezamos a tener una señal con un periodo mucho mayor (y por tanto una
frecuencia menor), más cercano a lo que puede percibir el ojo humano.
Dado que
en hemos profundizado bastante con el proceso P1, con el siguiente escrito en
la arquitectura – el P3 – seré más sintético en la explicación.
El
código del proceso P3 (bastante más sencillo) es:
Para
explicarlo volveré a recurrir a un diagrama de flujo:
Si lo analizamos bien tendríamos los siguientes
cronogramas:
De esto
se deduce que Tclk2 = 2 x Tclk1.
Como ya
calculamos Tclk1:
Tclk1
= 200 ms
Tenemos
que:
Tclk2
= 2 x Tclk1 = 400 ms = 0,4 segundos
---------------------------------------------------------------------------------
En el
siguiente proceso – el P2 – el programa empieza a procesar las salidas.
Recordemos
que habían cuatro salidas led1[0], led1[1], led1[2] y led1[3], definidas en un
vector de tipo std_logic_vector que se definió en la entidad como led1. Cada
una de estas salidas va a un pin configurado como output, y de este por una
pista de la Core3S250E al cátodo de un LED.
La
tensión de alimentación de los LEDs es de unos 3,3 voltios, y las salidas en
estado alto de los pines de la FPGA de unos 3,3 voltios en estado alto (o 1
lógico) y 0 voltios en estado bajo (o 0 voltios).
Un LED
es básicamente un diodo, los diodos conducen cuando la tensión del ánodo es más
grande que la del cátodo, es por ello
que, pese a que intuitivamente podamos identificar un 1 lógico -o estado alto-
como LED encendido, en realidad los LEDs conducen cuando los pines de salida
(gestionados por el vector led1) están en estado bajo o 0 lógico, ya que las
salidas están conectadas a los cátodos, no a los ánodos.
De
manera que si queremos que sólo se encienda el Led2 – por ejemplo – y que los demás permanezcan
apagados, tendríamos que poner led1[1] a 0 lógico, y led1[0], led1[2] y led1[3]
a 1 lógico. Es decir, la secuencia 1101 en el vector led1.
Lo que
hará el proceso P2, es mediante un contador introducir las secuencias
1110-1101-1011-0111 en el vector led1 sucesivamente, y que al incrementarse 4
veces (contador a 3 si se inicializa a 0) se vuelva a repetir la secuencia
1110-1101-1011-0111 (contador a 0), y así en bucle. Vemos como se implementa el
código para hacer esto.
El
diagrama de flujo es:
Si
puedes abrir una ventana dónde aparezca el código, y otra en la que aparezca el
diagrama de flujo, puedes entender bastante bien el código. Incluso si estás
introduciéndote en el VHDL puedes así aprender cómo se hacen ciertas cosas en este
lenguaje.
And the
last but not the least,
"Y con esto
y un bizcocho,
Hasta la
próxima entrada
Que es
la dieciocho."
(Aquí no
sólo hablamos de electrónica, hacemos incluso poesía).
0 comentarios: