Skip to content

Latest commit

 

History

History

hls

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

High Level Synthesis

En la siguiente sección se explicará el proceso para a partir de un código implementado en lenguaje C++, poder implementar un bloque (circuito lógico) que sea capaz de tomar dos entradas de 1024 valores x 8 bits, para entregar la distancia Euclidiana de ambos vectores. Este proceso de tomar un código de alto nivel (C++) y llevarlo a una implementación en FPGA se denomina High Level Synthesis.

En la Figura, se puede ver el bloque que se espera implementar. Como entrada, los vectores A y B, cada uno de 1024x8 bits. La salida, C, corresponde a un único valor de 26 bits.

Un análisis más fino del código utilizado para la síntesis de alto nivel, se puede encontrar en el README de la carpeta vitis_hls.

Referencias de como comunicarse con el coprocesador puede ser encontrada en el README dentro de la carpeta host_scripts.

Índice

Requisitos:

Para poder seguir las instrucciones que se listaran a continuación es necesario tener previamente instalado una versión de Vivado y Vitis HLS. Durante el desarrollo de este tutorial se estuvo trabajando con las versiones otorgadas por Xilinx: 2021.1

Si bien no es requisito, pero el hardware utilizado para el presente tutorial corresponde a una tarjeta de desarrollo: Nexys 4 DDR, con un chip xc7a100tcsg324-1.

Instrucciones:

Vitis HLS: Generación de módulo a partir de código de alto nivel

1. Creación de proyecto en Vitis HLS

Generación proyecto

En primer lugar se tiene que generar un nuevo proyecto dentro de Vitis HLS, para ello se puede hacer click en: "Create Project", esto desplegara un menú con opciones:

Para el nombre del proyecto / Project Name, se puede escoger cualquiera, en el caso de este tutorial se eligió: eucDis_. En la ubicación es recomendable escoger la carpeta vitis_hls que se encuentra dentro de los archivos del repositorio. En el caso de este ejemplo la carpeta completa del repositorio ha sido clonada en la carpeta de usuario (/home/mauricio/). Luego de configurar estos parámetros se da click a Next.

Añadiendo sources

En este menú se podrán añadir los códigos fuentes, en este caso, corresponden a los códigos en C++ que describen la funcionalidad que se busca, un módulo de cálculo de distancia Euclidiana. En primer lugar, haciendo click en Add Files, se añaden los archivos: eucMod.cpp y eucMod.h, que se encuentran dentro de la carpeta vitis_hls. Luego de esto, en el apartado denominado Top Function, se hace click en Browse para escoger la función principal, que en este caso corresponde a la denominada eucDis. Finalmente, se da click a next.

Añadiendo testbench

En esta sección se añaden las pruebas a las que se someterá tanto el código implementada y el módulo generado en contraste con el código de alto nivel original. Para ello se diseñan dos archivos que se deben añadir, desde la carpeta vitis_hls. En primer lugar, se debe añadir el archivo denominado testbench.cpp, este archivo contiene una prueba que compara el funcionamiento del código implementado en C++ para la obtención de la distancia Euclidiana entre los vectores A y B, y el módulo inferido a partir del código de alto nivel. En segundo lugar, se añade el archivo denominado goldenreference.csv, el cual tiene los vectores generados mediante el script goldenGenerator.py, los cuales son utilizados como entrada para el algoritmo de prueba. Detalles de la generación de estos valores de referencia pueden ser encontrados en el README de la sección utils.

Configuración de la solución y selección de hardware target

En esta sección se puede dar un nombre a la solución a implementar, en este caso se puede dejar el por defecto: solution1. Se puede dar la configuración del reloj que controlará la solución a implementar en este caso, se deja por defecto en 10 ns. Finalmente, en la sección de Part Selection, se selecciona el hardware target, en nuestro caso, se utiliza la placa de desarrollo Nexys 4 DDR, con un chip : xc7a100tcsg324-1, por lo que se ha seleccionado esta. Finalmente se le da click a Finish y se habrá configurado el proyecto de manera exitosa.

2. Simulación del código de alto nivel

Dentro de las herramientas ofrecidas por Vitis HLS, se encuentra la capacidad de comprobar el funcionamiento del código de alto nivel mediante una simulación. Esto se refiere a utilizar el testbench (testbench.cpp), para comprobar que el resultado del código sea el deseado.

Desde el panel de Flow Navigator, se puede escoger la opción Run C Simulation, esto generará la compilación y resultado del testbench que se incluyo en la estapa previa (testbench.cpp). Luego de esto, comenzara a compilarse y correr el código lo que se podrá observar mediante la actividad mostrada por la consola de Vitis HLS. Cuando se termine de correr el testbench, se desplegará un archivo con extensión .log que contendrá los resultados de la prueba al código de alto nivel. Para el testbench provisto se esperan los siguientes resultados:

INFO: [SIM 2] *************** CSIM start ***************
INFO: [SIM 4] CSIM will launch GCC as the compiler.
make: 'csim.exe' is up to date.
Running C++ Simulation!
-----------------------------------
Valor esperado: 3452
Valor calculado: 3452
Diferencia directa: 0
Diferencia relativa: 0
-----------------------------------
PASSED!
-----------------------------------
INFO: [SIM 1] CSim done with 0 errors.
INFO: [SIM 3] *************** CSIM finish ***************

Nota #1: Si bien el resultado del testbench se muestra tanto en la consola como en el archivo .log desplegado, siempre es mejor observar los resultados de la consola, dado que se ha encontrado ocasiones que luego de correr varias veces el testbench, el archivo log desplegado a veces no es actualizado, dando por resultado situaciones de falsos Passed/Failed.

Nota #2: Vitis HLS tiene la habilidad de desplegar ventanas emergentes con error asociado a los testbench, en el caso de que el código contenga un print con las palabras: "Error" o "Failed". Esto es bastante útil dado que es bastante claro cuando el comportamiento del código no es el esperado, sin la necesidad de trabajar con excepciones o alguna otra forma de aviso mediante código.

3. Síntesis

En esta etapa Vitis HLS, a partir del código de alto nivel, genera la implementación a partir del comportamiento asociado a la funcionalidad del código.

Para iniciar el proceso, desde el menú Flow Navigator, se escoge la opción Run C Synthesis.

Se desplegará un menú de opciones en el cual se puede confirmar parámetros del reloj asociado a la implementación y el hardware target. Estos corresponden a los escogidos anteriormente. Se procede a confirmar seleccionando OK, de esta forma iniciando el proceso de síntesis. Para el módulo provisto, se espera que el tiempo de síntesis sea de aproximadamente ~ 10 minutos.

Luego de completarse la síntesis, se desplegará un reporte con los resultados, principalmente de timming y uso de recursos estimados. El análisis de estos resultados se reportaran en una sección posterior.

4. Co-Simulación

Vitis HLS permite realizar la Co-Simulación entre el bloque descrito en un lenguaje de alto nivel y el inferido de este. Para ello se utiliza el testbench implementado para verificar el funcionamiento del código de alto nivel.

Para utilizarlo, desde el menú Flow Navigator, se hace click en Run Cosimulation.

Esto desplegará el menú de configuraciones para la cosimulación, se dejan todos los parámetros por defecto y se da click a OK, dando inicio al proceso.

Nota: El proceso de cosimulación puede ser bastante largo e intensivo en recursos del computador.

Luego de finalizarse el proceso, se desplegará un reporte, que en el campo Status informará si el módulo inferido pasó o no (Pass/Failed) el testbench. Además se entregará información del timming para la simulación.

5. Exportar a Vivado

En esta etapa, se puede exportar el módulo sintetizado desde Vitis HLS hacia Vivado para que sea utilizado como un módulo IP.

Desde el menú Flow Navigator, se escoge la opción Export RTL.

Se desplegará un sub-menú, en el cual se podrá escoger el directorio de salida (Output Location). Dentro del repositorio ya se incluye en la carpeta exported_ip un ejemplo del resultado de la exportación del módulo desde Vitis HLS. Como resultado del proceso, se debe obtener un archivo .zip que contiene el módulo. Antes de que este pueda ser usado en Vivado, se debe descomprimir, en el caso del zip provisto dentro de exported_ip, se puede descomprimir dentro de esta carpeta.

Nota: Se debe indicar la versión para el módulo dado que sino se encontrará un error al momento de realizar la exportación. Se puede poner un valor por defecto, en este caso se utiliza : 1.0.0

Vivado: Creación de Proyecto usando el módulo exportado

Para la generación del proyecto de Vivado, se hará una breve explicación de los parámetros del proyecto a generar, dado que no es el enfoque de este tutorial

1. Sources a incluir

  • Desde el botón Add Files se deben incluir todos los archivos con extensión .sv presentes en el directorio vivado

  • Desde el botón Add Directories se debe incluir la carpeta UART presente en la misma carpeta: vivado

2. Constraint

  • Dentro de los archivos entregados en la carpeta vivado, se encuentra un archivo constraint constraint_coprocessor.xdc el cual debe ser incluido con el proyecto.
3. Target Hardware

  • Siguiendo las instrucciones utilizadas en Vitis HLS, se selecciona como target la tarjeta de desarrollo Nexys 4 DDR: xc7a100tcsg324-1
4. Añadiendo Repositorio de IP

Las fuentes del proyecto provisto, incluyen previamente instanciado el módulo exportado desde Vitis HLS, este se encuentra en el archivo op_module.sv. Sin embargo, es necesario incluir el directorio (repositorio local) donde se encuentran los archivos extraídos desde el .zip exportado desde Vitis HLS.

Como se puede apreciar, al revisar las sources del proyecto, el módulo op_module.sv espera tener una instancia del módulo EUC_HLS, sin embargo no se encuentra el módulo asociado.

  1. Desde Project Manager se abre el IP Catalog.
  2. Haciendo clik derecho sobre el listado de repositorios se selecciona Add Repository, esto desplegara una ventana donde se puede seleccionar el directorio donde se ha descomprimido el .zip obtenido desde Vitis HLS. En el caso de este ejemplo se ha descomprimido en la carpeta /exported_ip.

  1. Luego de haber añadido el repositorio, aparecerá una sección denominada User Repository, dentro de la cual se encontrará el módulo IP a incorporar.

  1. Haciendo doble click sobre el módulo, abrirá una ventana de configuración, dar click a OK..

Nota: A la izquierda puede verse el módulo como bloque, se ve de esta manera, debido a que al realizar array_partition mediante pragmas (Veáse el README.md para más información), los puertos correspondientes a los vectores A y B, se consideran por elemento, dando que el módulo tenga 2048 entradas, sin contar las que se añaden de forma automática.

  1. Se desplegará un menú para la generación del módulo IP. Note que se da acceso a un Instatation Template, se recomienda utilizar éste como base en conjunto con el script e instrucciones para uso provistas en el README.md de la sección /hls/vitis_hls/utils, dado que asignar cada bus (vectores A y B) de forma individual para cada una de las 2048 entradas. Para finalizar, se da click en Generate.

  1. Luego de haber realizado estos pasos, al revisar en las design sources se puede observar que el módulo es correctamente listado.

  2. El proceso después continua normalmente con la síntesis, implementación y generación del bit stream.

Reportes de operación

A continuación se listan los siguientes reportes obtenidos, tanto desde Vivado, como de Vitis_hls.

Reporte desde Vitis HLS

Este reporte, se obtiene durante la etapa de diseño y solo representa el módulo a implementar y no el sistema completo, sin embargo, se considera una buena referencia para contrastar con los resultados finales.

Latencia ciclos BRAM % DSP % FF % LUT % URAM %
20 0 ~0 5 62 0

Reporte desde Vivado

Frecuencia de operación:

La implementación final, se realizó con un reloj principal de 100 MHz, para el diseño completo se obtuvieron los siguientes resultados de timming:

Worst Negative Slack ns Total Negative Slack ns Number of failing Endpoints Total Number of Endpoints
Setup 0.056 0 0 79565
Hold 0.023 0 0 79565

Latencia y Throughput

La latencia del módulo de distancia euclidiana implementado, puede ser verificado utilizando una ILA, instanciada en coprocessor.sv, conectada a los puertos op_enable y op_done del módulo de operaciones, de esta forma se verifica desde que se activa la señal enable hasta que se levanta la señal que informa que el resultado está disponible.

A partir de esto se verifica que la latencia corresponde a lo estimado durante la etapa de síntesis en Vitis_hls, 20 ciclos. De esta forma, considerando que se tiene un reloj de 100 MHz, se puede estimar que el throughput para el módulo implementado corresponde a 5.000.000 de operaciones por segundo. En resumen:

Latencia ciclos Latencia ns Throughput operaciones/s
20 200 5.000.000

Uso de recursos

A continuación se detalla el uso de recursos para la implementación completa del coprocesador:

Recurso Utilización Disponibles Utilización %
LUT 25669 63400 40.48738
LUTRAM 115 19000 0.6052631
FF 35937 126800 28.341484
BRAM 0.5 135 0.37037036
DSP 240 240 100.0
IO 21 210 10.0
BUFG 2 32 6.25

Tiempos desde síntesis en Vitis HLS a bitstream en Vivado

En esta sección se reportan los tiempos asociados a las distintas etapas del proceso de síntesis, implementación y generación de bitstream:

Programa Proceso Tiempo s Tiempo Acumulado s
Vitis HLS Simulación 11.23 11.23
Vitis HLS Síntesis 453.61 464.84
Vitis HLS Co-Simulación 615.32 1080.16
Vitis HLS Export RTL 406.09 1486.25
Vivado Síntetis 181 1667.25
Vivado Implementación 270 1937.25
Vivado Bitsream 40 1977.25

Por lo que un proceso completo, siguiendo el tutorial tomaría aproximadamente 32 minutos, de principio a fin.