SoC FPGA. La unión de CPU, µC y FPGA

Hoy en día las FPGAs no vienen solas, si no que en ocasiones van acompañadas de procesadores o microcontroladores, creando en su conjunto un SoC (System on a Chip). En el caso de la Cyclone V de Altera-Intel, la FPGA viene acompañada de un procesador ARM Cortex-A9, al que nos referiremos por HPS (Hard-Processor System), pero también se da la posibilidad de instanciar a Nios II, que es un softcore, es decir, es un procesador que se genera a través de las puertas lógicas dentro de la propia FPGA.
En el caso concreto que se está analizando, lo que trae consigo el SoC es la integración de dos paradigmas dentro de un mismo chip, con el consecuente ahorro de energía y de tamaño. En la placa de desarrollo tenemos la FPGA con una CPU y un conjunto de periféricos conectados. Algunos de esos dispositivos estarán conectados a la FPGA, otros irán conectados a la CPU, y tenemos total libertad para decidir qué queremos hacer y cómo.

Imagen 1. Arquitectura SoC Cyclone V.

En función de la finalidad vendrá mejor usar una arquitectura u otra. Así pues, se puede usar únicamente la FPGA, o únicamente el ARM con una distribución de Linux o una aplicación “Bare-Metal”, o llevándolo al siguiente nivel, podemos mezclar ambos mundos y quedarnos con lo mejor de cada parte. La pregunta ahora es, ¿cómo hacerlo?
Bueno, hemos dicho que hay periféricos que son propios de la FPGA o del ARM, pero para hacer una intercomunicación existen puentes. En este ejemplo básico vamos a desarrollar un sistema que permita ejecutar una aplicación desde Linux y acceder a recursos asociados a la FPGA.

Materiales necesarios

Hardware:

  • Placa de desarrollo: Terasic DE1-SoC
  • Tarjeta micro-SD
  • Cable mini-USB
  • Cable de red: RJ45

Software:

  • Quartus Prime 18.1
  • Intel SoC FPGA Embedded Development Suite (Incluye Quartus si no está instalado)
  • Win32DiskImager
  • Putty

Desarrollo

Lo ideal es hacerse con todos los materiales e instalar el software necesario de antemano. Entre las descargas y las instalaciones puede llevar un buen rato. Todo el software usado es gratuito, pero su código no es libre.

Preparar la micro-SD con la imagen

Por lo que he estado leyendo antes de leer el proyecto, no todas las imágenes compatibles con la placa de desarrollo son válidas para la finalidad de este proyecto. De hecho, he tenido que usar otra distinta a la que tenía con anterioridad. Desde la web de Terasic, accediendo al apartado de recursos disponibles para nuestra placa de desarrollo, se pueden encontrar diferentes versiones de Linux. Se recomienda usar la versión “Console Only”, es decir, sin escritorio, ya que es posible que en las otras no funcione.
Descarga de la imagen de Angstrom Linux para Terasic DE1-SoC
Una vez descargada la imagen la descomprimimos, ya que viene en un fichero zip. Insertamos la tarjeta micro-SD en nuestro PC y abrimos Win32DiskImager. Tan sólo hay que seleccionar la ruta de la imagen y la unidad de la tarjeta, es decir, el origen y el destino, y le damos a escribir. En menos de 5 minutos tendremos nuestra tarjeta lista para funcionar en la placa.
Para pasos posteriores, se va a realizar una comunicación entre el PC (Host) y la placa, así que la vamos a dejar ya configurada. Lo que hacemos es insertar la tarjeta micro-SD en su ranura y conectar un cable mini-USB a la UART de la placa y al puerto USB del PC. Lo que queremos es establecer una comunicación serie para acceder a la consola de comandos de Linux.

Levantar la red

Nos hará falta un programa como Putty, Hyperterminal o similar. Tenemos que mirar en el administrador de dispositivos qué puerto de comunicaciones está usando. En este caso es el COM5, pero en otro PC, podría estar usando otro diferente. En Putty hay que conectarse a este puerto de comunicación serie a una velocidad de 115200 baudios. En cuanto conectemos aparecerá una terminal de Linux donde podremos iniciar sesión. Por defecto el usuario es root y no tiene contraseña.

Imagen 2. Ubicando el puerto COM y configurándolo en PuTTY.

Conectamos la placa de desarrollo a un PC usando un cable de red. En el PC tenemos que tener libre un puerto RJ45. Le asignamos una dirección IP del mismo rango que la tarjeta de red del PC. Para eso se usa el siguiente comando: ifconfig [Interfaz] [IP]. En mi caso particular lo introduje de esta forma:
ifconfig eth0 192.168.10.33
Ya podemos acceder a la consola a través de SSH o Telnet. Para realizar la conexión, también se podría conectar a un switch o router en caso de no disponer de un puerto en el PC.

Construir el sistema

En el CD de Terasic, que también podemos obtener su contenido a través de la web, hay una herramienta que se llama System Builder. Es una forma muy sencilla de seleccionar los componentes de la placa de desarrollo que se quieren utilizar y que cree de forma rápida una plantilla para empezar a trabajar. La aplicación nos crea un proyecto de Quartus con algunas líneas de código ya escritas, pero la aplicación no sabe el diseño que vamos a hacer, así que hay líneas que nos sobrarán y tendremos que eliminar.

Imagen 3. Configurando el sistema en el System Builder.

Para continuar con el diseño del sistema pasamos a la utilidad de Quartus “Platform Designer”, anteriormente conocido como “Qsys”. En este caso queremos tener un clock, con un HPS y acceso a PIO, que se usará esa dirección para los leds.

Imagen 4. Diseñando la arquitectura del sistema en Platform Designer (anteriormente Qys).

La parte complicada viene a la hora de configurar el HPS. En principio nos podemos guiar por el GHRD (Golden Hardware Reference Design) que aporta el fabricante, pero a la hora de establecer los parámetros de las memorias y sus tiempos, la cosa es más compleja.
Una vez que tengamos el diseño en el Platform Designer, se puede guardar y exportar el código. Como la generación en el System Builder la ha hecho en Verilog, seguiremos usando ese lenguaje. Además, también podemos obtener una plantilla, sólo hay que darle a Generate-> Show Instantiation Template. De este modo no tendremos que escribir todo el código a mano. Al igual que antes, habrá líneas que no nos sean necesarias y las podamos borrar.

Imagen 5. Plantilla de código para la instanciación. Está incompleta, pero ahorra tiempo.

Por ahorrar tiempo, tanto el código como el fichero de del Platform Designer (.qsys) estará disponible para la descarga a través de GitHub. De modo que, si alguien quiere hacer el proyecto, están todas las configuraciones hechas y sólo tendrá que cambiar el dispositivo para el cual se programa.
Ahora lo que hay que hacer es “conectar los cables”. Si nos fijamos en la imagen de arriba, en la plantilla de código, pone algo así como “(<connected-to-…>)”. Pues tenemos que cambiarlo, ahí estamos instanciando al sistema que hemos creado, y tenemos que unirle los cables que hemos definido en el módulo. Quedaría algo del siguiente estilo, los nombres que aparecen arriba en módulo, los sustituimos abajo en la instanciación.

Imagen 6. Parte del código. Usé la plantilla y la edité.

Compilación

En primer lugar, hemos hecho cambios, así que vamos a comprobar solamente la sintaxis. Para ello le damos a “Start analysis and elaboration”. Si hay errores los corregimos, y en caso de que todo esté correcto, es hora de lanzar un par de scripts.
Los scripts nos van a ahorrar tiempo, ya que en vez de tener que introducir las direcciones de los pines a mano, lo va a hacer Quartus por nosotros. Hay que ejecutar los dos que aparecen en la imagen. ¡Ojo, que sólo añade los pines correspondientes al HPS!

Imagen 7. Scripts Tcl que hay que ejecutar.

Antes de compilar vamos a cambiar el comportamiento por defecto de los pines. Los ponemos como entrada tri-estado.

Imagen 8. Pasos a dar para cambiar el estado de los pines no usados.

Ahora ya podemos compilar todo el proyecto, pero también podemos limpiar todos los pines que no se usen del “Pin Planner”. Hay que mirar si la asignación está bien hecha, y si no introducirla a mano. Tras la compilación se generará un fichero con extensión .sof.

Generando los ficheros para Linux

Vamos a compilar el fichero sopcinfo. Este fichero contiene toda la información sobre los componentes que forman el sistema que hemos creado con el Platform Designer. El objetivo ahora es traducir este archivo en una cabecera, es decir, un fichero con extensión .h.
Hay que escribir el script “generate.sh”. Podemos encontrar un ejemplo en el CD de Terasic que hemos mencionado con anterioridad, pero habrá que abrirlo y editarlo para cambiar el nombre del archivo .sopcinfo del que queramos obtener sus cabeceras.
Para ello abrimos el Intel FPGA Embedded Command Shell. Básicamente es un intérprete de comandos basado en Linux.

Imagen 9. Llamada a las variables de entorno y generación de cabeceras.

Aquí se observa un error bastante habitual. Yo no tenía las variables de entorno para la compilación guardadas en Windows, y no tenía privilegios en el PC en el que me encontraba para modificarlas. Una alternativa es mediante el comando de “export PATH” que se ve en la imagen. En definitiva, que hay dos alternativas, o añadimos la ruta “C:\intelFPGA_lite\18.1\quartus\sopc_builder\bin\” (o donde tengamos instalado el EDS) a las variables de entorno de Windows, o le decimos en la consola dónde se encuentran las herramientas para compilar.
Tras ejecutar el script tenemos como salida las cabeceras necesarias.
A continuación, escribimos el programa en C. Va a ser una luz que va a ir saltando de led en led consecutivamente cada 250ms. Es decir, vamos a observar como una luz se va desplazando hacia la izquierda. El código también estará disponible para su descarga.
Una vez hecho esto, hay que generar un fichero de tipo Makefile. La plantilla también la podemos encontrar en el CD o en la web. En versiones anteriores era más sencilla, ahora han cambiado y hay que decirle la plataforma para la que se está trabajando. En nuestro caso, Cyclone V.

Imagen 10. Compilación en C y envío a la placa a través de SSH desde consola.

El programa ha compilado y tiene el nombre de “led”, así que lo que hacemos es enviarlo a la tarjeta a través de SSH. Recordemos que anteriormente hemos configurado la tarjeta para que esté conectada en red.

Configurando el arranque

Tras la compilación en Quartus, hemos dicho que se obtiene un fichero .sof. Este contiene la información necesaria para programar e inicializar la FPGA desde Quartus. En nuestro caso queremos inicializar la FPGA desde el arranque, es decir, desde un estado de apagado. Para eso necesitamos convertir el archivo sof (SRAM Object File) en RBF (Raw Binary File).

Lo primero que haremos será apagar la placa de desarrollo y extraer la tarjeta micro-SD, ya que habrá que copiar el archivo rbf resultante y hacer que lo cargue en el arranque.

En el CD podemos encontrar un script de Windows con el nombre de “sof_to_rbf.bat”, lo abrimos con un editor de texto y lo modificamos para que se correspondan los nombres de nuestros ficheros, de modo que se pueda hacer la llamada de forma correcta. Es fácil, sólo hay que cambiar el nombre del archivo .sof que tiene que leer y decirle el nombre con el que queremos que lo genere. Después de esto, lo guardamos y lo ejecutamos. Si todo va bien, nos creará el fichero .rbf y habremos terminado este paso.

Casi estamos terminando, ahora insertamos la tarjeta de memoria en nuestro PC y le copiamos el archivo rbf generado. Observamos que hay un archivo llamado “u-boot.src”, este es el que se encarga de decir qué es lo que se va a arrancar al inicio, así que hay que editarlo y decirle que queremos arrancar nuestro rbf, lo hacemos con un editor de texto.

Arranque y ejecución

Volvemos a insertar la tarjeta de memoria en la placa y nos disponemos a arrancar. La configuración del MSEL está en “010100”. Si estamos con la terminal abierta podremos ver qué es lo que está haciendo desde el arranque. En mi caso, he sacado esta captura que muestra cómo efectivamente está cargando el rbf que le he dicho:

Imagen 11. El sistema ha cargado el rbf al inicio.

Al igual que antes, aparecerá el intérprete de comandos. Iniciamos sesión y estaremos ya en nuestro directorio principal. Ahí es donde teníamos ya copiado el ejecutable “led”. Pues ahora lo abrimos y comprobamos el resultado. Vemos que los leds se mueven tal y cómo se especificó en el programa, por lo tanto, estamos accediendo desde Linux a recursos propios de la FPGA. Concluimos que el puente funciona correctamente, y que este ejemplo puede ser la base para desarrollar sistemas de mayor envergadura y complejidad.

Funcionamiento

Para terminar, dejo aquí un vídeo en el que se ve como funciona. Se ha cargado la imagen con el rbf y estamos ante el intérprete de comandos de Linux. Lo que hago es ejecutar el programa y ver cómo activa los leds.

Código Fuente

En los siguientes enlaces se puede encontrar el código generado en VHDL y el repositorio de adquisición de datos.

Repositorio de mis proyectos basados en SoC FPGA. El readme.md explica en qué consiste cada uno.
Código y ficheros generados para esta entrada. Demo de ejemplo con los leds.

Posibles problemas

Con este no me ha pasado, pero he hecho más proyectos basado en lo mismo y al principio me volví un poco loco, hasta que me di cuenta del problema y su solución. Cuando seguía el proceso y ejecutaba el programa en Linux, el sistema se quedaba colgado y tenía que apagar y encender. Dándole vueltas miré la asignación de pines en Quartus, y al parecer, aunque ejecutaba el script Tcl, no asignaba correctamente todos los pines. Tuve que cambiar las asignaciones de los periféricos y del reloj. Para eso lo único que hay que hacer es fijarse en los pines que vienen en la guía del usuario y compararlos con los que tenemos en el proyecto. Después de esto, volví a compilar, convertí el archivo .sof en .rbf, lo pasé a la tarjeta indicándoselo en el script de arranque “u-boot.src” y todo funcionaba.

 

 

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *