Si ya has programado en otros lenguajes (como por ejemplo en C), tienes bastante ganado porque verás que en VHDL se usan muchos conceptos de programación similares a otros lenguajes de programación, pero también hay diferencias importantes y no sólo de léxico y sintaxis.
En esta entrada abordaremos sólo dos, pero esas dos son claves porque si no se entienden bien pueden generar problemas al programador, estas dos cuestiones son: el concepto de concurrencia y diferencia entre variables y señales.
En esta entrada abordaremos sólo dos, pero esas dos son claves porque si no se entienden bien pueden generar problemas al programador, estas dos cuestiones son: el concepto de concurrencia y diferencia entre variables y señales.
Ambas son consecuencia de las diferencias que hay entre programar un microcontrolador o un procesador, y programar un sistema digital como son las FPGAs.
Concepto de concurrencia en VHDL:
Para entender el concepto de concurrencia repasemos como se trabaja en lenguajes de programación clásicos como el C o el C++ (C++ toma mucho del C, y este a su vez toma mucho de otros como el viejo Pascal).
En este tipo de lenguajes –llamemosles clásicos – nos encontramos que los códigos están escritos en forma secuencial (hasta aquí ninguna diferencia con VHDL), esto es:
Normalmente la ejecución de las instrucciones es secuencial (aquí ya empieza a haber diferencias con VHDL).
Es decir, si se ejecuta la instrucción i la siguiente será la instrucción i+1.
No siempre es así porque en los lenguajes de programación que hemos llamado clásicos, hay sentencias condicionales o de bifurcación (if-else, switch-case, etc…) de manera que si no se cumple una determinada condición en ese momento (o se cumple otra), entonces la siguiente instrucción a la instrucción i en ejecutarse puede ser la instrucción i+7 y no la instrucción i+1 necesariamente.
Además tenemos sentencias de recursividad o bucles (while, do-while, for…) de manera que a lo mejor la siguiente instrucción después de la instrucción i, sea la instrucción i-7 (por poner un ejemplo), e incluso es muy probable que muchas instrucciones escritas en el código se ejecuten más de una vez.
En cualquiera de los casos hay una condición que normalmente siempre se cumple:
---------------------------------------------------------------------------------
En los lenguajes como el C, lo más corriente es que en un instante de tiempo determinado, se ejecute una instrucción y sólo esa instrucción.
Así por ejemplo, si está ejecutandose la “ instrucción 5”, lo normal es que NO pueda estar ejecutándose la “ instrucción10” a la vez.
Dicho de otra forma, en los lenguajes como el C normalmente no se pueden ejecutar dos instrucciones al mismo tiempo.
---------------------------------------------------------------------------------
Y he dicho normalmente porque en muchos procesadores se trabaja con el concepto de pipeline, en el que de alguna manera (que no toca explicar ahora) se puede ejecutar instrucciones al mismo tiempo, pero eso es otra historia.
Y he aquí dónde aparece el concepto de concurrencia en VHDL, porque en VHDL si se pueden ejecutarse varias instrucciones a la vez (sin necesidad de tener que incorporar hardware adicional, como se hace para trabajar en el modo pipeline ya mencionado en los procesadores) . Es decir:
--------------------------------------------------------------------------------
En el lenguaje VHDL, en un instante de tiempo determinado, se puede ejecutar más de una instrucción.
Así por ejemplo, si está ejecutandose la “ instrucción 5”, SI puede estar ejecutándose la “ instrucción10” a la vez.
Dicho de otra forma, en VHDL se pueden ejecutar dos instrucciones o más al mismo tiempo.
---------------------------------------------------------------------------------
A esto es lo que llamamos concurrencia.
De hecho esto es lo habitual en VHDL, y sin embargo también se pueden incrustar trozos de código dentro del código VHDL en los que la ejecución sea “secuencial” de forma muy similar a los lenguajes de programación clásicos.
Dicho de otra forma,
---------------------------------------------------------------------------------
La concurrencia y la forma clásica “secuencial” de programación (como la del C o el C++) pueden coexistir en VHDL.
---------------------------------------------------------------------------------
Una forma de incrustar en el código VHDL partes del código que se ejecutan a la manera - digamos- tradicional (C,C++, Java… etc), es mediante los llamados procesos, los cuales de hecho usamos en el proyecto LEDA_blog.
La sintaxis de un proceso se podría resumir en el siguiente fragmento de código:
La lista de sensibilidad es una lista de señales (en el lenguaje VHDL signal) de manera que al cambiar alguno de los valores de las señales hace que se ejecute el proceso (PROCESS en la nomenclatura del VHDL).
Veámoslo mejor con un ejemplo.
La lista de sensibilidad es en este caso: x, y, z
Si alguna de estas señales (signal en el léxico de VHDL) se modifica se ejecuta el resto del código. Observe que las asignaciones a variables se hacen con los dos puntos seguido del igual “:=”, mientras que la asignación a señales se obtiene con el signo de menor y luego el de igual “<=”.
Con las diferencias en las asignaciones a variable y señal, ya vamos anticipando algo del siguiente apartado.
Diferencia entre variable y signal (señal) en VHDL:
Para entender la diferencia entre variable y señal empecemos por explicar el concepto de variable y como se declara.
Si tienes nociones de otros lenguajes de programación sabrás que las variables suelen ser posiciones de memoria a las cuales se les pueden introducir un tipo de dato en cualquier momento (incluyendo en su declaración). Estos tipos de datos pueden ser números enteros, reales… caracteres (‘A’ por ejemplo), cadenas de caracteres (“calamar” por ejemplo), bits (1 o 0 lógicos), cadenas de bits (“1011”)… etc.
En los lenguajes de programación – y en VHDL también – para hacer uso de una variable primero se declara, en VHDL la declaración seguiría la siguiente sintaxis:
VARIABLE identificador: tipo de dato;
Veamos un ejemplo:
VARIABLE count: INTEGER;
Hemos declarado una variable de tipo entero (integer) con el nombre count.
La palabra “count” es lo que hemos llamado identificador que es cualquier palabra o letra que queramos, siempre y cuando no coincida con una palabra reservada de VHDL como podrían ser VARIABLE o INTEGER que son parte de la semántica y de la sintaxis de este lenguaje (lo que suele aparecer en los códigos en azul).
Incluso podríamos a la vez que inicializamos la variable darle un valor inicial como sigue:
VARIABLE count: INTEGER := 2;
Gráficamente lo podemos simbolizar así:
Variable count:
2
|
Si en el resto del código hubiera una instrucción:
count := 5;
Entonces el valor guardado en la variable cambiaría inmediatamente a 5.
Variable count:
5
|
Las variables también se pueden inicializar en lugar de con un valor, con un rango de valores como por ejemplo:
VARIABLE count: INTEGER RANGE 0 TO 9;
O sea, la variable “count” puede ser cualquier entero en el rango entre 0 y 9 incluidos.
Ahora expliquemos las señales, y veremos que también almacenan datos – como las varibles - pero de una manera diferente. En las señales hay un valor actual con el que se trabaja en todas las instrucciones en las que se usa la señal, y cuando se le asigna un nuevo valor, este nuevo valor será con el que se trabajará (en un futuro próximo) en todas las instrucciones que hagan uso de dicha señal.
Me imagino que ahora mismo lo que acaba de leer le sonará a entelequia, pero espero dejarlo más claro a medida que desarrollemos más lo que queda de este post.
Las señales se declaran de forma parecida a las variables:
SIGNAL identificador: tipo de dato;
Por ejemplo:
SIGNAL sig_ex: INTEGER;
Le he dado el nombre al identificador de sig_ex por abreviar y no poner signal_example.
De una forma gráfica ahora lo que tendríamos es:
Las señales al igual que las variables se pueden inicializar en la misma declaración:
SIGNAL sig_ex: INTEGER := 5;
Con lo cual tendríamos:
Al igual que con las variables, se puede inicializar a la vez que se le asigna un valor inicial.
Si encontramos en el programa (líneas más abajo), una instrucción como por ejemplo:
sig_ex <= sig_ex*2;
Lo que tenemos ahora es:
Si vuelve a ejecutarse la instrucción:
sig_ex <= sig_ex*2;
Ahora tendremos:
Y así sucesivamente.
Sin embargo dónde es más esclarecedor la diferencia de señales (y dónde podemos cometer más errores si no tenemos las ideas claras), es en los trozos de código dónde la ejecución de instrucciones son secuenciales, como en los ya mencionados procesos.
Tomemos el ejemplo ya usado anteriormente.
Si hemos inicializado x, y, z = 1, y count = 0, los valores actuales que vamos ir teniendo son los que están en negrita en la parte izquierda de los recuadros, y los valores futuros los de la derecha de los recuadros en rojo:
Lo que tendremos una vez finalizado el proceso es que:
x = 2, y = 4, count = 1, y z = 2.
---------------------------------------------------------------------------------
Como se han modificado los valores de x, y, z, y las tres señales están en la lista sensible del proceso, el proceso volverá a ejecutarse.
En la siguiente iteración del proceso, lo que estaba en rojo a la derecha de los recuadros ahora pasa a negrita.
Es decir:
Lo que tendremos una vez finalizado el proceso es que:
x = 2, y = 5, count = 2, y z = 6.
---------------------------------------------------------------------------------
En la siguiente entrada daremos un repaso breve a la semántica y sintaxis del VHDL usando el código LEDA_blog.vhd.
0 comentarios: