Images

Análisis del código VHDL del proyecto LEDA_blog (V): Algoritmo implementado por el código.

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: