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