Acelerar, personalizar y optimizar Arduino
Hasta
ahora no hay ninguna razón por la que utilizar una FPGA para ejecutar
código Arduino: es mejor y más barato programar una Arduino convencional. En
este capítulo se van a discutir y explicar varias razones para emular
Arduino -o cualquier microcontrolador- en una FPGA:
1. Aceleración
Una
FPGA puede realizar operaciones complejas en uno o pocos ciclos de
reloj. Un microcontrolador está especializado en hacer un gran número de
operaciones sencillas por segundo. En algunos casos es muy beneficioso
resolver un problema con lógica reprogramable. Los ámbitos interesantes
son: criptología, tratamiento de imágenes, reconocimiento de voz,
radioastronomía...
Es una ventaja interesante especialmente para
la investigación. Hay pocos usos en los que una FPGA sea más rápido que
un microprocesador moderno o una GPU. En el ámbito Arduino (bajo
coste), no importa demasiado.
2. Obsolescencia
Este
problema se presenta especialmente en la industria: un microcontrolador
utilizado hace años en un proyecto aún en producción, se deja de
fabricar. Normalmente el código programado para microcontroladores están
especialmente concebidos para un modelo concreto e incluso con una
frecuencia de reloj concreta.
Este problema no existe si se emula
el microcontrolador en una FPGA. Aunque la FPGA se deje de fabricar, se
puede emular el mismo código, con el mismo comportamiento, en cualquier
modelo parecido de chip reprogramable.
En el ámbito de Arduino este problema no se suele dar: es más fácil cambiar el código.
3. Paralelismo
Esta es la primera ventaja que sí es interesante en el mundo hazlo-tú-mismo/Arduino.
Ejemplo I:
es necesario encender 8 LEDS exactamente a la vez, sin un retraso
mayor de un ciclo de reloj entre la primera y la última. Además, hay que
encender sólo algunos, dependiendo de una entrada.
Ejemplo II:
se está generando una señal con la Arduino que se transmite por radio.
Una modulación PWM cuya distorsión debe ser mínima. Además, hay que
generar 5 con distintas frecuencias. Además, hay que usar el micro para leer del puerto serie y otros trabajos que utilizan el microcontrolador por períodos indeterminados de tiempo.
Ejemplo III:
en un sistema que controla un robot se están utilizando 4 timers, 30
LEDS, 8 entradas analógicas y varios sensores que piden interrupciones
para dar su dato.
En estos tres ejemplos una Arduino convencional
estaría saturada o no sería capaz de cumplir requisitos. Un número
importante de interrupciones puede saturar el microcontrolador y, sobre
todo, convertir el desarrollo del proyecto en un auténtico infierno.
Una
solución podría ser la siguiente: un microcontrolador para cada sensor,
uno para cada salida PWM, uno para cada LED, uno para cada servo y cada
motor y una Arduino para gobernarlos a todos.
Con una FPGA es posible llegar a un término medio y
es una de sus aplicaciones más comunes: un microcontrolador gobierna
un sistema de módulos independientes con un sistema de comunicación entre
ellos eficiente y simple.
Entonces, en el ejemplo I se
programaría en VHDL un módulo que gestione 8 salidas digitales a la vez,
en el mismo ciclo de reloj, en el momento que le llegue la orden del
microcontrolador.
En el ejemplo II se programaría un módulo VHDL
que genera una señal PWM. Para ello utilizaría un contador, parecido al
timer del microcontrolador pero sin interferir con él. Para generar 5
señales sólo hay que instanciar este módulo 5 veces y conectarlos con
las salidas correspondientes. Cualquier pin físico sería válido.
En
el ejemplo III se generaría un módulo en VHDL para cada timer, módulos
de gestión de cada sensor, etc. El microcontrolador sólo tendría que
atender las entradas de usuario y enviar los mensajes correspondientes a
cada módulo. Todo a máxima velocidad y mínima saturación del micro.
La
comunicación entre módulos se hace utilizando las direcciones de ram
externa que no están siendo utilizadas en el proyecto AVR8. Se definen
tantos registros como sea necesario para cada módulo.
4. Personalización
Tener
el código del microcontrolador a utilizar tiene sus ventajas. Es
posible, por ejemplo, aumentar el número de salidas digitales o
analógicas (y hacerlo de forma más eficiente gracias al punto anterior).
En el siguiente ejemplo se crea una Arduino emulada y se modifica para tener 48 salidas pseudo-analógicas PWM.
- 48 módulos independientes con un temporizador de una resolución de 8 bits.
- Cada módulo se conecta a un pin de la Papilio.
- 48 direcciones de memoria externa reservadas para la comunicación entre Arduino y los módulos.
- Si el valor en cada registro es mayor que el contador, a la salida del módulo se pone un uno. Si no, un cero.
Con la herramienta Oruga (pendiente de publicación) es posible crear puntos base para desarrollar este tipo de proyectos
Herramienta Oruga
Además, se baja el módulo PWMout.vhd que está preparado para ser utilizado con un proyecto Oruga. Es un módulo que recoge un valor de 8 bits de un registro de memoria (cuya dirección es definida en el momento de instanciarlo) y lo utiliza para generar una señal PWM con un ciclo de trabajo proporcional al valor que le llegue.
En el siguiente código (auto-generado por Oruga) se instancia el módulo que se configura con la dirección de memoria E/S 0x0FE0 y se conecta con el pin físico 0 del puerto A. Además, se configura ese pin para ser de salida en la última línea.
OrugaA_0:component Oruga_PWMout
GENERIC MAP(
io_base_address_generic => x"0fe0")
PORT MAP(
nReset => nrst,
clk => clk16M,
adr => core_adr,
dbus_in => core_dbusout,
iore => core_iore,
iowe => core_iowe,
output_sig => porta(0)
);
DDRAReg(0)<='0';
Se genera un proyecto con 48 pines conectados a instancias de módulos PWMout. Por defecto se reservan dos direcciones de memoria para cada módulo. El primer módulo tendrá las direcciones 0xFE0 y 0xFE1, el segundo 0xFE2 y 0xFE3 y así hasta el último módulo: 0x103e y 0x103f. Esto es así para poder utilizar un byte de escritura y otro de lectura, pero en este caso sólo es necesario enviar datos al módulo PWM (sólo se usan las direcciones pares).
El esquema con los 48 módulos es peculiar:
El código Arduino inicial va a ser:
void setup()
{
for (int i = 0; i<48; i++){
writePWM(i, 256*i/48);
}
}
void loop()
{
}
Junto con el fichero Oruga.pde, que contiene lo siguiente:
#define io_start_address 0x0FE0
byte writePWM(int pin, byte value){
_SFR_IO8(io_start_address+pin*2) = value;
}
Este sketch va a configurar el estado inicial de cada módulo PWM con código Arduino, por lo que la configuración va a hacerla el microcontrolador, módulo a módulo.
Se puede observar que en 800 microsegundos (menos de un milisegundo) se han configurado los 48 canales, cada uno con un valor distinto de ciclo de trabajo, con 3 líneas de Arduino y ni una sola de VHDL, gracias a la herramienta Oruga. Además, la función loop() de Arduino está vacía, por lo que el microcontrolador está totalmente libre para otros trabajos.