Algo que nos aportan las FPGA en nuestros diseños es la flexibilidad. Esta flexibilidad no viene solo en forma de poder redireccionar los pines del dispositivos de la forma que más nos convenga, sino que nos permite implementar dentro de estas casi cualquier diseño digital que se nos ocurra, siempre que (1) dispongamos del espacio suficiente y (2) siempre que se cumplan los requisitos de tiempos que nuestro diseño requiere.

Uno de los elementos que podemos implementar en las FPGA son procesadores o microcontroladores. En la actualidad existen muchos (muchos, muchos) procesadores que podemos implementar en FPGA. Estos procesadores se conocen como soft-cores o procesadores softare(?), y tenemos procesadores con arquitecturas RISC, x86, MIPS… además de procesadores con uno o varios núcleos. Los fabricantes de FPGA saben de esto, y en sus entornos de desarrollo vienen incluidos algunos procesadores para incluirlos en sus FPGA. Tenemos el caso de Mi-V en FPGAs de Microchip, los cuales son softcores configurables basados en RISCV, o Microblaze en el caso de Xilinx, los cuales son procesadores RISC. En este artículo vamos a ver como crear el diseño hardware con procesador Microblaze en la tarjeta Arty de Digilent.

Crear el proyecto.

Antes de crear el proyecto, lo primero que tenemos que hacer es añadir la tarjeta Arty a nuestro repositorio local de tarjetas, de forma que podamos utilizarla en nuestro proyecto. Para añadir la tarjeta Arty a nuestro repositorio local de tarjetas, lo podemos hacer desde Vivado, entrando en la Xilinx Store, a la cual podemos acceder desde la ventana de bienvenida de Vivado. Una vez dentro, nos vamos a la pestaña Boards, y buscamos la tarjeta Arty o Arty A35T. Cuando la tengamos localizada, pulsamos sobre ella con el botón derecho y la instalamos.

Xilinx store

Una vez tenemos añadida la tarjeta a nuestro repositorio local, ya podemos empezar a crear el proyecto, para ello pulsamos sobre Create project. Una vez seleccionado el nombre y la ubicación, lo siguiente es seleccionar el tipo de proyecto. En este caso queremos generar un proyecto RTL, y para ahorrar algo de tiempo, podemos marcar las casillas que indican que no queremos añadir ni código fuente ni archivos de restricciones al proyecto por ahora.

Project type

Ahora nos toca seleccionar la pieza que vamos a utilizar en el proyecto. En este caso, como vamos a utilizar una tarjeta de desarrollo, nos iremos a la pestaña Boards, y buscaremos aquí la tarjeta Arty que hemos descargado previamente. Cuando la encontremos la seleccionamos y le damos a siguiente.

Select part

En la siguiente ventana podremos ver un resumen del proyecto. Lo aceptamos y Vivado nos creará el proyecto y lo abrirá.

El proyecto que vamos a desarrollar va a estar basado en Microblaze, por lo que es infinitamente más sencillo hacer el diseño en un block design. Para crear un block design, en la columna de la izquiera pulsamos sobre Create block design, le asignamos un nombre, y se abrirá una ventana en blanco donde podemos empezar a añadir los diferentes bloques.

Configurar los relojes del diseño.

El primer bloque que vamos a añadir es el bloque del reloj. La tarjeta Arty lleva un [reloj(https://digilent.com/reference/programmable-logic/arty-a7/reference-manual?redirect=1#oscillatorsclocks) externo de 100 MHz conectado al pin E3. No hay ningún problema en utilizar este reloj como reloj del sistema, pero como veremos un poco más adelante, vamos a necesitar varios relojes en nuestro diseño, y aunque es posible genenrarlos desde el bloque Memory Interface Generator (MIG), la precisión que nos da el MCMM externo es mejor, por lo que esta será la opción que utilizaremos. Por tanto, desde la pestaña Board, añadiremos el elemento System Clock.

Componentes de la tarjeta

Esto nos añadirá al diagrama una entrada de reloj, y un bloque Clocking Wizard, el cual tiene un reloj de entrada, y uno de salida. para generar los diferentes relojes, hacemos doble click sobre el bloque. En la primera pestaña configuramos el reloj de netrada, el cual por defecto está confgiurado a 100 MHz, que es la frecuencia del reloj de la Arty. En la segunda pestaña configuramos los relojes de salida. Aquí solo tenemos un reloj configurado por defecto. para este diseño nos harán falta 3 relojes diferentes:

  • Reloj de 166 Mhz como reloj principal del MIG.
  • Reloj de 200 Mhz para la entrada ref_clk del MIG.
  • Reloj de 25 Mhz para el PHY Ethernet.

La configuración quedará de la siguiente manera.

Configuración de reloj

Además de los relojes de salida, tenemos que cambiar el flanco del reset a Reset negado, y como no lo vamos a utilizar, podemos deseleccionar la salida de locked.

Interfaz memoria RAM DDR3.

Una vez tenemos los relojes necesarios para el diseño configurados, lo siquiente que vamos a añadir al block design es el controlador de la memoria RAM. para agregar el MIG tenemos que arrastrar desde la pestaña donde encontramos los elementos de la tarjeta el periférico DDR3. Esto añadirá al proyecto un MIG configurado correctamente para la memoria DDR3 que lleva la tarjeta. Este bloque nuevo que hemos añadido tiene diferentes entradas y salidas. En cuanto a las entradas y salidas de reloj tenemos:

  • sys_clk_i donde irá conectado el reloj principal del diseño de 166 MHz.
  • clk_ref_i en esta entrada conectaremos el reloj de 200 MHz que necesita el MIG.
  • ui_clk en esta salida de reloj tenemos una frecuencia igual a la frecuencia de la DDR dividida entre 4, en esta configuración será de 81 MHz.
  • ui_addn_clk_0 Esta es una salida de reloj auxiliar generada por el MCMM del MIG. Esta salida se puede utilizar para obtener el reloj de 200 MHz necesarios para el clk_ref_i, pero en nuestro caso estamos obteniendo este reloj del MCMM principal, por lo que la dejaremos si conectar.

Cuando agregamod el MIG, podemos pulsar sobre Run Block Automation de forma que nos generará el interfaz externo para la DDR, y conectaré el reset. A continuación, conectaremos el reloj de 166 MHz a la entrada sys_clk_i, y el reloj de 200 MHz a la entrada clk_ref_i. El MIG quedará conectado de la siguiente manera.

Configuración del MIG

El resto del diseño utilizará el reloj ui_clk de 81 MHz como reloj prinicipal, ya que necesitamos que el diseño funcione síncrono con la DDR.

A continuación ya podemos incluir en el diseño el soft-core Microblaze.

Configurar el soft-core Microblaze.

Para añadir el soft-core pulsaremos sobre el botón + y buscaremos Microblaze en el recuadro de búsqueda. Nos aparecerán varias opciones, de las cuales tenemos que seleccionar Microblaze.

Incluir Microblaze

Una vez añadido, nos saldrá el pop-up para ejecutar el Run Block Automation, pulsando sobre el pop-up podremos confgiurar el preset de microbvlaze que queremos configurar. tenemos tres opciones disponibles:

  • Microcontrolador de propósito general
  • Microcontrolador en tiempo real para ejecutar cálculos o procesamiento de señal.
  • Microcontrolador de aplicación que incluye una memory management Unit (MMU) para la gestion de memorias de forma que podremos utilizarlo para correr Linux.

En esta guía podéis encontrar información sobre las tres configuraciones disponibles.

En este caso seleccionaremos el preset Microcontrolador. Además de la memoria RAM DDR3, podemos añadir algo de memoria local. Esta memoria está implementada utilizando BlockRAM de la FPGA y no va conectada al interfaz AXI, por lo que el acceso a esta será más rápido. Este tipo de memoria se parece a las memorias Tighly Coupled Memory (TCM) que disponemos en microcontroladores como los ARM Cortex R5. la capacidad de esta dependerá de la aplicación, pero para poder hacer pruebas, con 32 kB será suficiente. por otra parte necesitamos que el puerto AXi esté habilitado, ya que será como nos comuniquemos con la DDR así como con el resto de periféricos, y por último también necesitaremos habilitar el controlador de interrupciones. En el último desplegable seleccionaremos la fuente del reloj principal del Microblaze, que como hemos dicho será la salida ui_clk del MIG.

Preset Microblaze

Una vez configurado, procederemos a conectarlo todo pulsando sobre Run Connection Automation y veremos que se crean varios bloques adicionales para la gestión del reset del microcontrolador y el controlador de interrupciones.

BD Microblaze

Una vez tenemos todo lo necesario para hacer funcionar el Microblaze, lo siguienjte que haremos será añadir los diferentes periféricos.

Añadir periféricos.

Para añadir los diferentes periféricos de la tarjeta, desde la columna de la izquierda iremos arrastrando los que vayamos a ncecesitar. Para este diseño añadiremos:

  • 4 LEDs para controlar 4 leds de la tarjeta mediante un periférico GPIO conectado al bus AXI.
  • 4 PushButton para poder leer el estado de los 4 pulsadores a través de un periférico AXI GPIO.
  • USB UART que añadirá un periférico AXI UART Lite para poder enviar y recibir datos mediante la conexión USB.
  • Ethernet MII que añadirá un periférico AXI Ethernet Lite para manejar el interfaz Ethernet con una velocidad de 10/100 Mbps.

Una vez añadidos estos periféricos, pulsamos sobre Run Connection Automation, y se añadirán todos al bus AXI através del bloque AXI Interconnect. Además de esto, tenemos que conectar las interrupciones del periférico AXI Ethernet Lite y AXI UART al bloque concat que va conectado al controlador de interrupciones.

A continuación, necesitamos sacar fuera de la FPGA el reloj de 25 MHz que utiliz el PHY Ethernet. para ello pulsando botón derecho sobre el reloj y seleccionando Make External nos generará el puerto.

Ethernet Clock

Una vez hecho esto, el diseño de bloques quedará de la siguiente manera.

BD Microblaze completo

Lo siguiente será generar el archivo de restricciones para el puerto de reloj del PHY.

Generar el archivo de restricciones.

Si vamos al punto donde se describe la interfaz ethernet en el manual de Digilent, podemos ver que el reloj del PHY está conectado al pin G18 de la FPGA. Para conectar este reloj a este pin, tenemos que crear un nuevo archivo de restricciones (Add Sources > Add or Create Constraints File), y añadir el siguiente código al archivo.

set_property PACKAGE_PIN G18 [get_ports clk25mhz]
set_property IOSTANDARD LVCMOS33 [get_ports clk25mhz]

Mapa de memoria.

Por último, abriendo la pestaña Address Editor podemos ver el mapa de memoria del microcontrolador.Vemos que cada uno de los periféricos que hemos añadido tiene configurada una dirección base, y un tamaño. Además vemos disponemos de 256M que es donde idealmente vamos a almacenar nuestro código fuente.

por otra parte vemos que Vivado divide los bloques de memoria por redes (Networks), de forma que todos los periféricos que van conectados al mismo bus AXi, los tenemos agrupados en la Network0, mientras que la memoria local la tenemos ubicada en la Network 1.

Estas direcciones, sobre todo las de las memorias DDR y Local habrá que tenerlas en mente cuando vayamos a configurar el archivo Linker, en el cual confgiuraremos qué funciones ejecutaremos desde la DDR, y qué otras nos interesará ejecutar en la memoria Local.

Mapa de memoria

Implementando el diseño.

Una vez tenemos el diseño listo, lo que haremos será generar el código HDL que implementa todo lo que hemos ido añadiendo al diseño en forma de bloques, para ello pulsamos botón derecho sobre el archivo del Blockk Design y seleccionamos Create HDL Wrapper.

Generación Wrapper

Una vez tenemos generado el wrapper, lo único que nos queda es implementar el diseño. para ellos pulsamos sobre Generate Bitstream, y seleccionamos el número de jobs que queremos ejecutar simultáneamente. Cuando tenemos un diseño de bloques, cada uno de los bloques puede sintetizarse de formma independiente, por lo que si seleccionamos 6 jobs, Vivado lanzará 6 hilos de síntesis simultáneos, ahorrandonos algo de tiempo. Obviamente el número de jobs máximos simultáneos que podemos ejecutar será igual al número de hilos de ejecución de nuestro procesador.

Implementando el diseño

Una vez el diseño ha sido generado, necesitamois generar un archivo que contenga toda la información del hardware, de forma que nuestro IDE de desarrollo de C sepa de los periféricos que dispone y de la confgiuración de estos. Para ello, desde Vivado, tenemos que exportar el hardware. Esto lo hacemos desde File > Export > Export Hardware

Exportando el diseño

A la hora de exportar el diseño, tenemos que configurar que incluya el bitstream en el diseño, de forma que podremos cerrar Vivado y configurar tanto la FPGA como el diseño software desde Vitis.

En este punto tenemos nuestro hardware listo para empezar a utilizar la trajeta Arty como un microcontrolador con varios periféricos.