viernes, 16 de septiembre de 2011

Tutorial emulación/simulación de Arduino en FPGA - Índice y Conclusiones


Este tutorial se ha creado como complemento a la charla titulada Codiseño y cosimulación basado en FPGAs para plataforma Arduino del Open Source Hardware Convention 2011.

Tiene seis partes:



Conclusiones*:
  1. Emular el microcontrolador de Arduino o cualquier otro en una FPGA permite personalizar ancho de buses, número y tipo de salidas y entradas, frecuencias de reloj, tamaño de memoria o puertos de comunicación e inhabilitar los recursos que no sean utilizados para optimizar y adaptar el hardware al proyecto (y no viceversa).
  2. Simular el proyecto completo, hardware y software, a nivel de señal desde un PC sin necesidad de programación en una FPGA es una ventaja tremenda hasta para proyectos básicos de Arduino.
  3. Existen otras ventajas de utilizar hardware reconfigurable como son la capacidad de procesado o la protección de obsolescencia pero no son tan importantes en el ámbito DIY/Hardware Libre.
 
Trabajos futuros:
  1. Crear una herramienta open-source que permita configurar un sistema completo, añadir módulos con funcionalidades, comunicaciones, co-procesamiento, etc. El prototipo se llama Oruga y está escrito en Javascript: http://oruga.tk
  2. Crear módulos en HDL (VHDL, Verilog...) administrados desde Arduino, independientes de ellas o conectados a otros módulos como, por ejemplo:
    1. Salida PWM con frecuencia configurable.
    2. Entrada RC-PPM de varios canales desde una misma señal.
    3. Salida VGA.
    4. Memoria externa.
    5. Generadores de ondas.
    6. Entrada analógica.
    7. Comunicación genérica de protocolos serie como I2C o SPI.
    8. Generación y mezcla de sonido y otras funciones DSP.
    9. Módulos de coprocesamiento.
*Las conclusiones se ponen junto al índice porque son un buen resumen.

jueves, 15 de septiembre de 2011

Tutorial emulación/simulación de Arduino en FPGA - Parte VI

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.

  1. 48 módulos independientes con un temporizador de una resolución de 8 bits.
  2. Cada módulo se conecta a un pin de la Papilio.
  3. 48 direcciones de memoria externa reservadas para la comunicación entre Arduino y los módulos.
  4. 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.









miércoles, 14 de septiembre de 2011

Tutorial emulación/simulación de Arduino en FPGA - Parte V

Compilar programas Arduino para simulación

En capítulos anteriores se ha simulado un programa Arduino previamente cargado. Para programar la FPGA con un sketch personalizado conviene diferenciar dos métodos:
  1. Modificar los bits directamente de la memoria en la FPGA y reemplazar el sketch cargado.
  2. Modificar el código inicial con el que se sintetiza el diseño del microcontrolador.
El primer método sólo sirve para la ejecución del sketch pero no habría forma de probarlo previamente. Es la forma de programar con el Arduino IDE modificado por Gadget Factory. Sólo hay que seleccionar la "board" aplicable en el menú Tools de Arduino. Por ejemplo Gadget Factory Papilio One 500K Board.

Para proyectos complejos es necesario simular y para ello el código del sketch debe ir previamente escrito en el proyecto (en VHDL).

El código inicial se guarda en bloques de memoria que lleva la FPGA, (de tipo BRAM). En el core se instancian bloques de tipo RAMB16_S18, que es un componente que proporciona Xilinx. En la hoja de características de la FPGA hay más explicaciones sobre los diferentes tipos de memoria que se pueden instanciar.

En el código suministrado se instancia el componente de memoria 8 veces: RAM_Word0 a RAM_Word7. En el código de inicialización de la instancia (ver fichero sources/Memory/XPM8Kx16.vhd) es posible dar los datos iniciales de cada bit, son los registros INIT_00 a INIT_3F.


RAM_Word0:component RAMB16_S18
generic map (
INIT => X"00000", -- Value of output RAM registers at startup
SRVAL => X"00000", -- Ouput value upon SSR assertion
WRITE_MODE => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE
-- The following INIT_xx declarations specify the intial contents of the RAM
-- Address 0 to 255
INIT_00 => PM_Inst_RAM_Word0_INIT_00,
INIT_01 => PM_Inst_RAM_Word0_INIT_01,
INIT_02 => PM_Inst_RAM_Word0_INIT_02,
...


En cada registro se pueden almacenar 64 dígitos hexadecimales, es decir 4x64 = 256 bits. En cada bloque hay 64 (3F) registros, por lo que hay 16.384 bits en cada bloque. Juntando los 8 bloques inicialmente instanciados, se pueden utilizar 16KBytes de memoria para programas Arduino.

PM_Inst_RAM_Word0_INIT_XX son constantes, que están definidas en el fichero sources/Memory/prog_mem_init.vhd:

constant PM_Inst_RAM_Word0_INIT_00 : bit_vector(0 to 255) := x"0053940C0053940C0053940C0053940C0053940C0053940C0053940C0030940C"; constant PM_Inst_RAM_Word0_INIT_01 : bit_vector(0 to 255) := x"0053940C0053940C0053940C0053940C0053940C0053940C0053940C0053940C";
...

Los caracteres hexadecimales son el sketch compilado para ser utilizado en ATmega.

En el fichero hardware\tools\butterfly_platform\Makefile del Arduino IDE modificado, se modifica la siguiente línea y se cambia por la ruta correcta:

PAPILIO_SIM_PATH = "C:/Arduino-Soft-Core/sources/Memory/prog_mem_init.vhd"

Si alguna de las carpetas tiene un espacio, es importante añadirle el carácter \ antes.

Ahora se selecciona la Gadget Factory Papilio Custom Board del menú Tools y al pulsar "Upload" se generará el nuevo fichero prog_mem_init.vhd.



Sólo queda repetir los pasos de los capítulos anteriores del tutorial para simular el sketch.

En el siguiente capítulo se modificará el diseño del proyecto para crear módulos de aceleración.


Parte I - Introducción
Parte II - Emulación del modelo RTL de Arduino
Parte III - Simulación con ModelSIM (si se usa una versión de pago)
Parte IV - Simulación con ISim (integrado en Xilinx ISE)
Parte V - Compilar programas Arduino para simulación
Parte VI - Acelerar, personalizar y optimizar Arduino

miércoles, 31 de agosto de 2011

Tutorial emulación/simulación de Arduino en FPGA - Parte IV

En este capítulo se va a simular el proyecto con ISim, por si no se cuenta con una versión de pago de ModelSIM.

El procedimiento es mucho más sencillo que con ModelSIM. Hay que asegurarse de haber seleccionado ISim como simulador a la hora de crear el proyecto.

Para poder estimular a la Arduino se necesitan, por lo menos, dos cosas: un reloj y una señal de reset. Esto se hace desde un banco de trabajo o testbench. En el código de Gadget Factory se facilita uno, en scripts\XilinxISE\testbench.vhd. En el menú Project se selecciona "Add copy of source" y se busca este fichero.

Antes de simular se deben hacer algunos cambios, abriendo el testbench con doble click:

Cambiar

constant clk_period : time := 1us;

por

constant clk_period : time := 31.25 ns;

Para que el reloj sea de 32Mhz.

Además hay que comentar estas dos líneas:

portb <= "11111111";
portd <= "10101010";

Que deben quedar de esta forma:

--portb <= "11111111";
--portd <= "10101010";

El programa de Arduino que vamos a grabar para la prueba es el siguiente:

int ledPin = 13; void setup() {
pinMode(ledPin, OUTPUT);
}

void loop()
{
digitalWrite(ledPin, HIGH); // set the LED on

digitalWrite(ledPin, LOW); // set the LED off

}


Que simplemente hace parpadear el pin 13 constantemente.


En la ventana Files de ISE se edita prog_mem_init.vhd y se reemplazan las siguientes líneas:

constant PM_Inst_RAM_Word0_INIT_00 : bit_vector(0 to 255) := x"00B1940C00B1940C00B1940C00B1940C00B1940C00B1940C00B1940C008E940C"; constant PM_Inst_RAM_Word0_INIT_01 : bit_vector(0 to 255) := x"00B1940C00B1940C00B1940C00B1940C00B1940C00B1940C00B1940C00B1940C"; constant PM_Inst_RAM_Word0_INIT_02 : bit_vector(0 to 255) := x"00B1940C00B1940C00B1940C00B1940C00B1940C00B1940C00B1940C013B940C"; constant PM_Inst_RAM_Word0_INIT_03 : bit_vector(0 to 255) := x"0039000000270023003200350038003B000000280022003100340037003A0000"; constant PM_Inst_RAM_Word0_INIT_04 : bit_vector(0 to 255) := x"0303030303030202020202020202010101010101010100200021003000330036"; constant PM_Inst_RAM_Word0_INIT_05 : bit_vector(0 to 255) := x"2010080402010606060606060606050505050505050504040404040404040303"; constant PM_Inst_RAM_Word0_INIT_06 : bit_vector(0 to 255) := x"2010080402018040201008040201804020100804020180402010080402018040"; constant PM_Inst_RAM_Word0_INIT_07 : bit_vector(0 to 255) := x"0000000000000500000100000000000000000000000080402010080402018040"; constant PM_Inst_RAM_Word0_INIT_08 : bit_vector(0 to 255) := x"BE1F241100000000000000000000000000000000000000000000000000000000"; constant PM_Inst_RAM_Word0_INIT_09 : bit_vector(0 to 255) := x"9631920D95D8C004BF0B9503EF0FE0F3E3E8E0B0E6A0E010BFCDBFDEE0DFEFCF"; constant PM_Inst_RAM_Word0_INIT_0A : bit_vector(0 to 255) := x"940C0134940EF7E107B136AB921DC001E0B0E6A2E010BE1BF7C907B136A2F3C8"; constant PM_Inst_RAM_Word0_INIT_0B : bit_vector(0 to 255) := x"00609180950800EC940EE0600060918000EC940EE061006091800000940C019A"; constant PM_Inst_RAM_Word0_INIT_0C : bit_vector(0 to 255) := x"4F3F57262D9095C82FF92FE84F9F54862F932F82E0302F28950800C4940EE061"; constant PM_Inst_RAM_Word0_INIT_0D : bit_vector(0 to 255) := x"95C896312DA095C84FFF5AE01FFF0FEEE0F02FE8F0A923882D8095C82FF32FE2"; constant PM_Inst_RAM_Word0_INIT_0E : bit_vector(0 to 255) := x"2F952F84E0502F489508938C2B89918C9508938C23899590918CF42923662DB0"; constant PM_Inst_RAM_Word0_INIT_0F : bit_vector(0 to 255) := x"4F5F57462D9095C82FF92FE84F9F54862F952F842D2095C82FF92FE84F9F5186"; constant PM_Inst_RAM_Word0_INIT_10 : bit_vector(0 to 255) := x"B58FF4213024C004778FB58FF4193023F0B12322F16923332D3095C82FF52FE4"; constant PM_Inst_RAM_Word0_INIT_11 : bit_vector(0 to 255) := x"E0F02FE3BD857D8FB585F4193025C005BF837D8FB783F4213021C00BBD8F7D8F"; constant PM_Inst_RAM_Word0_INIT_12 : bit_vector(0 to 255) := x"9508938C23899590918CF42923662DB095C896312DA095C84FFF59E21FFF0FEE"; constant PM_Inst_RAM_Word0_INIT_13 : bit_vector(0 to 255) := x"2411920FB60F920F921FCFFD00B3940E00BE940E0183940E9508938C2B89918C"; constant PM_Inst_RAM_Word0_INIT_14 : bit_vector(0 to 255) := x"006A9130006991B0006891A0006791900066918093BF93AF939F938F933F932F"; constant PM_Inst_RAM_Word0_INIT_15 : bit_vector(0 to 255) := x"939000669380006A93201DB11DA19601572DF020372D5F2D2F231DB11DA19601"; constant PM_Inst_RAM_Word0_INIT_16 : bit_vector(0 to 255) := x"1DB11DA19601006591B0006491A00063919000629180006993B0006893A00067"; constant PM_Inst_RAM_Word0_INIT_17 : bit_vector(0 to 255) := x"BE0F900F912F913F918F919F91AF91BF006593B0006493A00063939000629380"; constant PM_Inst_RAM_Word0_INIT_18 : bit_vector(0 to 255) := x"BD8E6081B58EBD8E6082B58EBF876081B787BF836084B78394789518901F900F"; constant PM_Inst_RAM_Word0_INIT_19 : bit_vector(0 to 255) := x"000000000000000DCFFF94F89508BD856081B585BD856082B585BD8F6081B58F"; constant PM_Inst_RAM_Word0_INIT_1A : bit_vector(0 to 255) := x"0000000000000000000000000000000000000000000000000000000000000000"; constant PM_Inst_RAM_Word0_INIT_1B : bit_vector(0 to 255) := x"0000000000000000000000000000000000000000000000000000000000000000";

El programa sólo ocupa estas líneas, desde INIT_00 hasta INIT_19. El resto de líneas se rellenan con ceros. La explicación de compilar programas Arduino y convertirlos en este formato se hará más adelante.

Una vez guardado el fichero, se vuelve a la ventana Design y se selecciona la vista Simulation. Al seleccionar testbench, se habilita el proceso Simulate Behavioral Model. Al hacer doble click sobre él se abre ISim.




En la barra de herramientas


se escribe 300us y se pincha en el tercer icono, "Run for the time specified on the toolbar".

En el visor de señales se comprueba perfectamente que el pin 13 (pin 5 del puerto B) parpadea tal y como estaba previsto.



Se puede comprobar que digitalWrite es una función muy lenta ya que tarda 15 microsegundos en cambiar de estado.





Parte V - Compilar programas Arduino para simulación
Índice del tutorial

martes, 30 de agosto de 2011

Tutorial emulación/simulación de Arduino en FPGA - Parte III

En esta parte del tutorial se va a utilizar ModelSIM PE o SE. Lamentablemente el Student Edition (gratuito) está limitado a 10.000 líneas de código y sólo el proyecto AVR8 tiene 16.000. Es posible simular unos cuantos ciclos con él pero es muy muy lento.



Antes de abrir ModelSIM, en ISE es necesario seleccionar el nodo xc3s500e-4vq100 del árbol en la ventana de diseño. La ventana de procesos muestra una serie de acciones y seleccionar "Compile HDL Libraries". Esto va a tardar unos minutos.




Ya en ModelSIM, al iniciar un nuevo proyecto hay que seleccionar la configuración generada por ISE. En el mismo fichero del proyecto ISE hay un fichero llamado modelsim.ini, que dice a ModelSIM dónde encontrar las librerías que se han compilado previamente.


Una vez completado el resto del formulario, se añaden ficheros al proyecto. Se seleccionan directamente del directorio del proyecto ISE, excepto FrqDiv.vhd, que no se utiliza.

Aparecerá una lista de ficheros en la ventana Project. En cualquiera de ellos, seleccionar, con el botón derecho, Compile - Compile Order.


Hacer click en Auto Generate. Debería decir "46 compiles, 0 failed with no errors".



Ahora es necesario añadir un testbench. Es un fichero que estimula el proyecto y sin el cual no haría nada. Gestiona la simulación de entradas y salidas físicas y señales como el reset y el reloj. En la carpeta scripts\XilinxISE del AVR8 hay un testbench.vhd, que hay que añadir al proyecto.

Antes de simular se deben hacer algunos cambios, abriendo el testbench con doble click:

Cambiar

constant clk_period : time := 1us;

por

constant clk_period : time := 31.25 ns;

Para que el reloj sea de 32Mhz.

Además hay que comentar estas dos líneas:

portb <= "11111111";
portd <= "10101010";

Que deben quedar de esta forma:

--portb <= "11111111";
--portd <= "10101010";

El programa de Arduino que vamos a grabar para la prueba es el siguiente:

int ledPin = 13; void setup() {
pinMode(ledPin, OUTPUT);
}

void loop()
{
digitalWrite(ledPin, HIGH); // set the LED on

digitalWrite(ledPin, LOW); // set the LED off

}


Que simplemente hace parpadear el pin 13 constantemente.

Se edita prog_mem_init.vhd y se reemplazan las siguientes líneas:

constant PM_Inst_RAM_Word0_INIT_00 : bit_vector(0 to 255) := x"00B1940C00B1940C00B1940C00B1940C00B1940C00B1940C00B1940C008E940C"; constant PM_Inst_RAM_Word0_INIT_01 : bit_vector(0 to 255) := x"00B1940C00B1940C00B1940C00B1940C00B1940C00B1940C00B1940C00B1940C"; constant PM_Inst_RAM_Word0_INIT_02 : bit_vector(0 to 255) := x"00B1940C00B1940C00B1940C00B1940C00B1940C00B1940C00B1940C013B940C"; constant PM_Inst_RAM_Word0_INIT_03 : bit_vector(0 to 255) := x"0039000000270023003200350038003B000000280022003100340037003A0000"; constant PM_Inst_RAM_Word0_INIT_04 : bit_vector(0 to 255) := x"0303030303030202020202020202010101010101010100200021003000330036"; constant PM_Inst_RAM_Word0_INIT_05 : bit_vector(0 to 255) := x"2010080402010606060606060606050505050505050504040404040404040303"; constant PM_Inst_RAM_Word0_INIT_06 : bit_vector(0 to 255) := x"2010080402018040201008040201804020100804020180402010080402018040"; constant PM_Inst_RAM_Word0_INIT_07 : bit_vector(0 to 255) := x"0000000000000500000100000000000000000000000080402010080402018040"; constant PM_Inst_RAM_Word0_INIT_08 : bit_vector(0 to 255) := x"BE1F241100000000000000000000000000000000000000000000000000000000"; constant PM_Inst_RAM_Word0_INIT_09 : bit_vector(0 to 255) := x"9631920D95D8C004BF0B9503EF0FE0F3E3E8E0B0E6A0E010BFCDBFDEE0DFEFCF"; constant PM_Inst_RAM_Word0_INIT_0A : bit_vector(0 to 255) := x"940C0134940EF7E107B136AB921DC001E0B0E6A2E010BE1BF7C907B136A2F3C8"; constant PM_Inst_RAM_Word0_INIT_0B : bit_vector(0 to 255) := x"00609180950800EC940EE0600060918000EC940EE061006091800000940C019A"; constant PM_Inst_RAM_Word0_INIT_0C : bit_vector(0 to 255) := x"4F3F57262D9095C82FF92FE84F9F54862F932F82E0302F28950800C4940EE061"; constant PM_Inst_RAM_Word0_INIT_0D : bit_vector(0 to 255) := x"95C896312DA095C84FFF5AE01FFF0FEEE0F02FE8F0A923882D8095C82FF32FE2"; constant PM_Inst_RAM_Word0_INIT_0E : bit_vector(0 to 255) := x"2F952F84E0502F489508938C2B89918C9508938C23899590918CF42923662DB0"; constant PM_Inst_RAM_Word0_INIT_0F : bit_vector(0 to 255) := x"4F5F57462D9095C82FF92FE84F9F54862F952F842D2095C82FF92FE84F9F5186"; constant PM_Inst_RAM_Word0_INIT_10 : bit_vector(0 to 255) := x"B58FF4213024C004778FB58FF4193023F0B12322F16923332D3095C82FF52FE4"; constant PM_Inst_RAM_Word0_INIT_11 : bit_vector(0 to 255) := x"E0F02FE3BD857D8FB585F4193025C005BF837D8FB783F4213021C00BBD8F7D8F"; constant PM_Inst_RAM_Word0_INIT_12 : bit_vector(0 to 255) := x"9508938C23899590918CF42923662DB095C896312DA095C84FFF59E21FFF0FEE"; constant PM_Inst_RAM_Word0_INIT_13 : bit_vector(0 to 255) := x"2411920FB60F920F921FCFFD00B3940E00BE940E0183940E9508938C2B89918C"; constant PM_Inst_RAM_Word0_INIT_14 : bit_vector(0 to 255) := x"006A9130006991B0006891A0006791900066918093BF93AF939F938F933F932F"; constant PM_Inst_RAM_Word0_INIT_15 : bit_vector(0 to 255) := x"939000669380006A93201DB11DA19601572DF020372D5F2D2F231DB11DA19601"; constant PM_Inst_RAM_Word0_INIT_16 : bit_vector(0 to 255) := x"1DB11DA19601006591B0006491A00063919000629180006993B0006893A00067"; constant PM_Inst_RAM_Word0_INIT_17 : bit_vector(0 to 255) := x"BE0F900F912F913F918F919F91AF91BF006593B0006493A00063939000629380"; constant PM_Inst_RAM_Word0_INIT_18 : bit_vector(0 to 255) := x"BD8E6081B58EBD8E6082B58EBF876081B787BF836084B78394789518901F900F"; constant PM_Inst_RAM_Word0_INIT_19 : bit_vector(0 to 255) := x"000000000000000DCFFF94F89508BD856081B585BD856082B585BD8F6081B58F"; constant PM_Inst_RAM_Word0_INIT_1A : bit_vector(0 to 255) := x"0000000000000000000000000000000000000000000000000000000000000000"; constant PM_Inst_RAM_Word0_INIT_1B : bit_vector(0 to 255) := x"0000000000000000000000000000000000000000000000000000000000000000";

El programa sólo ocupa estas líneas, desde INIT_00 hasta INIT_19. El resto de líneas se rellenan con ceros. La explicación de compilar programas Arduino y convertirlos en este formato se hará más adelante.

Botón derecho en cualquier fichero de la pestaña Project - Compile All. Mientras no se está familiarizado con un diseño es recomendable compilar todos los ficheros siempre. Cuando hay más confianza, es más rápido utilizar "Compile out-of-date".

En el menú Simulate, seleccionar "Start Simulation". Se elige work.testbench y se dejan todas las opciones como están.

Se añaden unas cuantas señales al visor de ondas tal y como se indica en la siguiente figura, haciendo click derecho en testbench, Add, To Wave, All items in region.



Si no sale la ventana Wave, seleccionarla desde el menú View. La simulación debería hacer parpadear el pin 13 (pin 5 del puerto B).

(Pendiente de publicar el resultado de la simulación.)


Parte V - Compilar programas Arduino para simulación
Índice del tutorial

Tutorial emulación/simulación de Arduino en FPGA - Parte II

En los siguientes capítulos del tutorial se va a describir la forma de simular un programa Arduino. El objetivo es ejecutar un sketch de Arduino sin utilizar la placa, emulando el microcontrolador en un ordenador.

Para ello se puede utilizar ModelSIM o ISim de Xilinx. El Student Edition de ModelSIM está limitado y no se recomienda. Sin un ModelSIM de pago, utilizaremos ISim, que no es tan completo pero es gratuíto y suficiente para el cometido del tutorial.

Captura general de ModelSIM

Captura general de ISim

¿Qué es simular un diseño de lógica reprogramable?
La simulación en este tipo de software se realiza a muy bajo nivel, visualizando directamente señales digitales dentro del chip, no variables u objetos. El objetivo de la simulación es depurar y comprobar el correcto funcionamiento de un diseño antes de implementarlo físicamente en un chip, ya sea una FPGA o un circuito integrado de propósito concreto.

El funcionamiento de las FPGA u otros dispositivos hardware de lógica reconfigurable está fuera del propósito de este tutorial pero, resumiendo, son circuitos integrados que se configuran para actuar como cualquier otro circuito integrado. Esta versatilidad se paga en precio y en algunos casos, rendimiento.

¿Cómo se carga un sketch en el modelo emulado?
La FPGA utilizada tiene unos cuantos bloques de memoria BRAM. La memoria de programa y datos del microcontrolador también se van a emular, utilizando estos bloques. El código compilado de un sketch se transforma al formato apropiado con una herramienta de Xilinx llamada data2mem.

Uso de Xilinx ISE Webpack
Vayamos a simular con ModelSIM o ISim, antes hay que cargar el proyecto en Xilinx ISE.

El proyecto AVR8 se baja y descomprime en una carpeta temporal. En el momento de escribir este tutorial la última versión es GadgetFactory Arduino Soft Core v1.6-0. Se abre el Project Navigator y se crea un nuevo proyecto. En este paso hay que seleccionar un tipo de FPGA. Si se va a utilizar más tarde la Papilio, habrá que elegir por ejemplo XC3S500E para la Papilio 500k. También es importante elegir en este punto (aunque luego se puede cambiar) el simulador. ModelSIM si se cuenta con una versión de pago e ISim, si no.

En el menú Project se elige "Add copy of source". Todos los ficheros de la carpeta sources del con extensión vhd y ucf se añaden. Son unas cuantas carpetas y hay que ir una por una.

En la ventana Design, con la opción Implementation seleccionada, se genera un árbol de instancias de componentes y componentes sin instanciar: xc3s500e-4vq100 es la FPGA. Papilio_AVR8 es la instancia de la Arduino, o del microcontrolador de la Arduino.



¿Significa esto que es posible instanciar varias Arduinos en una sola FPGA? Sí, en ésta en concreto, caben cuatro.

FrqDiv, XDM*** y XPM*** son componentes que se han añadido y no se han instanciado. El resto de componentes cuelgan de Papilio_AVR8. Al expandir su rama salen una serie de componentes instanciados y otros ficheros de configuración. Hay dos que salen con una interrogación, Inst_DCM32to16 y papilio_core_template_COMP. Uno está en la carpeta descomprimida previamente scripts\XilinxISE\ipcore_dir y otro en submodules\papilio_core_template\sources. Se añaden.

Para entender mejor el microcontrolador es recomendable seleccionar Papilio_AVR8 en el árbol de diseño y seleccionar el proceso "View RTL Schematic", después seleccionar "Start with a schematic of the top-level block".

Se generará el siguiente diagrama:



Haciendo doble click sobre un bloque, se abre. Abriendo Papilio_AVR8 se puede empezar a ver las tripas del microcontrolador. Control + rueda del ratón hace zoom.



Es interesante ver que hay un componente llamado AVR_Core_Inst (instancia de AVR_Core) que gestiona todos los buses, enables, etc. Sus periféricos son módulos como PM_Inst que es la memoria de programa (donde vamos a cargar el sketch) o los módulos de RAM utilizados en la ejecución de programas, en principio vacíos. DCM32to16 convierte el reloj de 32Mhz de la Papilio en 16Mhz, que es la frecuencia correcta para Arduino.




También es interesante fijarse en que hay diferentes tipos de puertos para cada instancia de puerto en la Papilio.



El estudio gráfico de un diseño hardware con esta herramienta es bastante más sencillo que analizar el código VHDL por lo que es recomendable intentar entender qué hace cada módulo si más tarde se va a modificar el hardware para adaptarlo a alguna necesidad concreta.

Siguientes partes:
Parte III - Simulación con ModelSIM (si se usa una versión de pago)
Parte IV - Simulación con ISim (integrado en Xilinx ISE)
Índice del tutorial 

jueves, 25 de agosto de 2011

Tutorial emulación/simulación de Arduino en FPGA - Parte I

Introducción
Hay que entender que el hardware de Arduino es un microcontrolador muy común, con su memoria, multiplexores, registros, buses, etc. No es más que un circuito digital. En sitios como opencores.org podemos encontrar modelos RTL de muchos microprocesadores. Es una descripción abstracta de lo que un circuito digital hace.


ATmega2560 por dentro (físicamente)

Ejemplo: un inversor digital da a su salida el valor negado de la entrada. En esta descripción abstracta no se habla de tecnología utilizada de transistores, de encapsulados o de medidas de rendimiento como el tiempo de respuesta.


Representación de un modelo de inversor



Seis inversores en un encapsulado

Si cada componente de un microcontrolador se define de esta forma y se establecen las conexiones entre los cientos o miles de componentes, se puede crear un modelo RTL bastante complejo pero funcional.



Representación esquemática de componentes y conexiones en un ATmega103 modificado

  • Un modelo RTL se crea con lenguajes de descripción de hardware (HDL) como son VHDL o Verilog.
  • Un modelo RTL sirve para simular el comportamiento de un circuito hardware con ayuda de un ordenador.
Es la forma de diseñar hardware hoy. No puede haber un prototipo físico en cada cambio que haya en el proceso de diseño de un microprocesador o microcontrolador.

Una FPGA es un dispositivo hardware que se configura con un modelo RTL. Esto significa que es posible hacer que una FPGA se comporte como un microcontrolador determinado y, por lo tanto, hacer que se comporte como cualquier circuito digital. Este ejercicio se llama emulación.
  • El objetivo de este tutorial es emular un microcontrolador ATmega103 en una FPGA y hacer que ejecute programas Arduino.
  • Además, se optimizarán programas para crear procesos paralelos y se personalizará el hardware para, por ejemplo, crear salidas analógicas PWM en cualquier pin.

Software utilizado
Xilinx ISE Webpack 13.1 para sintetizar el diseño. Incluye ISim para simularlo.
Opcional: ModelSIM SE o PE para simularlo (no sirve el Student Edition)
AVR8 Source Code V1.6. Código fuente en VHDL del ATmega103 modificado por Gadget Factory.
Butterfly Arduino IDE. IDE de Arduino 0018 modificado por Gadget Factory.




Parte II - Emulación del modelo RTL de Arduino
Índice del tutorial