0% encontró este documento útil (0 votos)
8 vistas116 páginas

Red Neuronal en FPGAs: Proyecto Fin de Grado

Este proyecto consiste en la implementación de una red neuronal artificial en una FPGA. Se desarrollan los componentes básicos necesarios para el funcionamiento de la red neuronal de forma modular. Se implementan módulos como la función sigmoid y los controladores de neuronas, capas y la red completa. Como comprobación, se muestra una red neuronal que implementa la puerta lógica XNOR. Se definen líneas futuras de mejora.

Cargado por

edu pitas3000
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
8 vistas116 páginas

Red Neuronal en FPGAs: Proyecto Fin de Grado

Este proyecto consiste en la implementación de una red neuronal artificial en una FPGA. Se desarrollan los componentes básicos necesarios para el funcionamiento de la red neuronal de forma modular. Se implementan módulos como la función sigmoid y los controladores de neuronas, capas y la red completa. Como comprobación, se muestra una red neuronal que implementa la puerta lógica XNOR. Se definen líneas futuras de mejora.

Cargado por

edu pitas3000
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

UNIVERSIDAD POLITÉCNICA DE MADRID

Escuela Técnica Superior

de Ingeniería de Sistemas Informáticos


MADRID

Proyecto Fin de Grado

de Ingeniería de Computadores

Red Neuronal Artificial en FPGAs


Autor: Javier Ebrero Barrantes

Tutor: Vicente A. García Alcántara

Co-tutor: Samuel Barrado Aguirre

Julio de 2019
Índice
Índice ..................................................................................................................................... I

Objetivo ................................................................................................................................ V

Resumen ............................................................................................................................. VII

Abstract................................................................................................................................ IX

Capítulo 1: Introducción ......................................................................................................... 1

Capítulo 2: Conocimientos Previos........................................................................................... 3

2.1 Redes Neuronales. ............................................................................................................... 3

2.1.1 Inspiración Biológica. ....................................................................................................... 4

2.1.2 Modelo Matemático......................................................................................................... 6

2.1.3 Aplicaciones. ................................................................................................................... 12

2.1.3 Software y librerías......................................................................................................... 14

2.2 FPGAs................................................................................................................................. 16

2.2.1 ¿Por qué una FPGA? ....................................................................................................... 18

2.2.2 Arquitectura de una FPGA. ............................................................................................. 21

2.2.3 FPGAS en el mercado...................................................................................................... 24

2.2.4 Aplicaciones. (Xilinx, 2019) ............................................................................................. 27

2.3 VHDL. ................................................................................................................................. 31

2.3.1 Diseño y Estructura......................................................................................................... 32

2.3.2 Sintaxis. .......................................................................................................................... 36

2.3.3 Vivado Xilinx. .................................................................................................................. 39

2.3.4 Otros lenguajes HDL. ...................................................................................................... 39


I
Capítulo 3: Desarrollo ........................................................................................................... 41

3.1 Arquitectura ...................................................................................................................... 41

3.2 Implementación en VHDL .................................................................................................. 47

3.2.1 Neuron ............................................................................................................................ 47

3.2.2 Sigmoid ........................................................................................................................... 51

3.2.3 Neuron Top ..................................................................................................................... 56

3.2.4 Layer Generation ............................................................................................................ 60

3.2.5 Top Layers .................................................................................................................. 63

Capítulo 4: Resultados .......................................................................................................... 69

4.1 Simulación de la entidad neuron ....................................................................................... 69

4.2 Simulación de la entidad sigmoid...................................................................................... 71

4.3 Simulación de la entidad neuron top ............................................................................ 73

4.4 Simulación de la entidad Layer generation ................................................................... 76

4.5 Simulación de la entidad top Layers.............................................................................. 79

Capítulo 5: Planificación........................................................................................................ 87

Capítulo 6: Aspectos éticos, sociales y ambientales................................................................. 89

6.1 Identificación ..................................................................................................................... 89

6.2 Descripción ........................................................................................................................ 89

6.3 Análisis............................................................................................................................... 89

Conclusiones........................................................................................................................ 93

Líneas Futuras ...................................................................................................................... 95

Volcado del código en una FPGA ............................................................................................. 95

II
Implementación del algoritmo “Backpropagation” ................................................................ 95

División de la estructura en varias FPGAs ............................................................................... 95

Optimización de la solución propuesta ................................................................................... 96

Glosario de Acrónimos.......................................................................................................... 97

Bibliografía ......................................................................................................................... 99

III
IV
Objetivo
El objetivo de este proyecto es la implementación de una red neuronal artificial ya entrenada
en cualquier FPGA. Para la realización de este proyecto, se tendrán en cuenta varios hitos:

1. Estudio previo de conocimientos básicos necesarios en el ámbito de inteligencia


artificial, más específicamente, en el apartado de redes neuronales artificiales.

2. Realizar una propuesta a nivel arquitectónico (distintas entidades, algoritmos y la


conexión entre ellos).

3. Desarrollar el código fuente necesario para la solución propuesta.

4. Por último, la demostración del correcto funcionamiento tanto, de la propuesta, como


del código realizado mediante el uso de una simulación funcional.

V
VI
Resumen
Este proyecto consiste en la realización de una red neuronal artificial con una FPGA (Field
Programming Gate Array, del español, matriz de puertas programables). Para ello, en la primera
iteración del proyecto se realizarán los componentes básicos necesarios para el correcto
funcionamiento de la red neuronal artificial. Para poder desarrollar este proyecto han sido
necesarios unos conocimientos previos en una materia específica como es la inteligencia
artificial y el lenguaje de descripción hardware VHDL, los cuales, se han reflejado en el apartado
de conocimientos previos del proyecto.

Por otro lado, en la parte de desarrollo, se ha realizado una arquitectura modular bottom-to-
top (del español, de más bajo a más alto) que consiste en el desarrollo de componentes más
pequeños y básicos, a más grandes y complejos. Cada módulo es independiente del resto y su
función está totalmente acotada a un objetivo, como por ejemplo, controlar otro módulo,
realización de un algoritmo, etc. De esta manera se consigue un sistema escalable, fácil de
comprobar, con posibilidades de mejoras y de conexión con otros proyectos. Entre todos los
módulos cabe destacar varios de ellos, como pueden ser, la realización de la función sigmoid
(del español, función sigmoide) y los módulos encargados de controlar las distintas neuronas,
capas y la totalidad de la red neuronal.

Como comprobación de todo lo realizado, en el capítulo de resultados, se muestra la estructura


de una red neuronal que implementa la puerta lógica XNOR mediante la estructura generada
en este proyecto.

Para finalizar, se definen unas líneas futuras en las que se añaden nuevas características con
mejoras a nivel de rendimiento, escalabilidad y funcionalidades para alargar la vida útil del
proyecto.

VII
VIII
Abstract
This project consists in the realization of an artificial neural network with a FPGA (Field
Programming Gate Array). At the first iteration of the project it will be done the basic
components in order to the properly functioning of the artificial neural network. To be able to
do this project, it has been necessary a previous knowledge in a specific ambit such as the
artificial intelligence and the hardware description language VHDL, which has been reflected at
the background knowledge chapter.

On the other hand, at the development part it has been accomplished a modular architecture
bottom-to-top which consist in the develop of basic and smaller components to more complex
and bigger. Each module is independent of others and its task is limited to a specific target, such
as control another module, doing an algorithm and so on. Then, in this way, a scalable system
is achieved, easy to check, better possibilities for improvement and it is easier to connect with
other projects. Among all the modules it is worth mentioning several of them, like the develop
of the sigmoid function and the entities in charge of controlling the distinct neurons, layers and
the whole neural network.

As a check of everything done, at the results chapter, the structure of a neural network which
implement the XNOR logic gate is shown through the structure generate in this project.

Finally, future lines are defined in which new characteristics with performance improvement,
scalability and functionalities are added in order to lengthen the useful life of the project.

IX
X
Capítulo 1: Introducción
Este proyecto se ha realizado para estudiar la viabilidad del uso de otros tipos de hardware para
el tratamiento de enormes cantidades de datos de una empresa grande como el BBVA (Banco
Bilbao Vizcaya Argentaria) (BBVA, 2019), mediante el uso de la IA (Inteligencia Artificial)
(Adarraga & Zaccagnini, 1994), más en concreto, Redes Neuronales Artificiales (Ng, 2018).

El BBVA es una entidad bancaria española, la cual, consta de un activo de 676.689 millones de
euros de euros a 31 de diciembre de 2018, siendo la segunda entidad financiera española por
volumen de activos. A su vez, cuenta con 7.963 oficinas, 125.627 empleados y 74,5 millones de
clientes, estando presente en más de 30 países.

Debido a lo dicho anteriormente, el BBVA quiere encontrar un modo de dar servicio a todos sus
clientes de manera específica ofreciendo un trato particular a cada uno.

Para poder realizar este servicio, lo ideal sería aplicar lo que haría un empleado del BBVA a un
cliente, pero en vez de mediante un recurso humano, empleado, este, sería una máquina. Esta
sustitución se basa en el proceso que se denomina Aprendizaje de Máquinas (del inglés,
machine learning) (Ng, 2018).

Por consiguiente, este proyecto específicamente se centrará en tratar los conocimientos


necesarios para poder realizar el modelado hardware de una ANN (del inglés, Artificial Neural
Network), por lo que se tratarán los conocimientos necesarios a nivel matemático, lógico,
arquitectural y funcional.

Para ello, se han realizado varias líneas de investigación, búsqueda de conocimiento sobre redes
neuronales, varios cursos y proyectos de machine learning y la búsqueda de algoritmos
implementados para operaciones básicas en dichos aspectos.

Otra de las finalidades de este proyecto es el uso de hardware específico para el desarrollo de
redes neuronales, ya que todas las implementaciones extendidas son mediante el uso de CPUs
(del inglés, Central Processing Unit) y GPUs (del inglés, Graphics Processing Unit).

1
2
Capítulo 2: Conocimientos Previos
Las principales tecnologías aplicadas en este proyecto son el uso de Redes Neuronales
Artificiales, FPGAs (Xilinx, 2019) como medio procesamiento y controlador, por último, el
lenguaje de descripción Hardware VHDL (VHSIC (Very High Speed Integrated Circuit, del
español, Circuito integrado de muy alta velocidad ) Hardware Description Language, del
español, Lenguaje de descripción hardware). Por tanto, se va a proceder a revisar someramente
las mismas.

2.1 Redes Neuronales.


Una red de neuronas artificial (Frontiers, 2019) es un conjunto interconectado de nodos que
simulan la red de neuronas de un cerebro biológico. Cada nodo simula una neurona artificial y
las flechas son las conexiones con otras neuronas artificiales. A continuación, en la siguiente
figura, se muestra una representación de una red de neuronas.

Figura 2.1.1. Red neuronal artificial

3
Históricamente, el uso de redes de neuronas fue utilizado con frecuencia a partir de los años
80s y hasta 90s, periodo en el cual, hubo un decrecimiento en la investigación y uso de las
mismas.

Su abandono fue debido a que el hardware y el entendimiento de las redes de neuronas no era
suficiente, por lo cual, se quedaron en el olvido hasta hoy en día que, con la era del Big Data
(macro datos, datos masivos, inteligencia de datos o datos a gran escala) (Marr, 2014) se han
vuelto a usar asiduamente ya que es una de las mejores formas de tratar tanta cantidad de
datos de manera correcta para dar un resultado adecuado. En la siguiente figura se puede
observar el proceso genérico que se realiza cuando tienes una cantidad masiva de datos o Big
Data.

Figura 2.1.2. Proceso de aprendizaje de máquinas. (Alias, 2018)

2.1.1 Inspiración Biológica.


Para simular un cerebro humano el cual, es muy complejo, es necesario saber cómo realizar los
mismos procesos que hace una neurona para trasmitir, procesar y enviar estímulos y a su vez
como las conexiones que crean entre varias neuronas conforman lo que de denomina
aprendizaje.

A continuación, se procede a desarrollar los pasos que hace una neurona en función de lo dicho
anteriormente.

4
En primer lugar, uno de los elementos principales son las dendritas (Lexikon, 2003), que son
prolongaciones protoplásmicas ramificadas bastante cortas de la neurona, dedicadas
principalmente a la recepción de estímulos y, secundariamente, a la alimentación celular. Son
terminales de las neuronas; y sirven como receptores de impulsos nerviosos provenientes
desde un axón perteneciente a otra neurona. Su principal función es recibir los impulsos de
otras neuronas y enviarlas hasta el soma de la neurona.

Lo más importante de esta definición es la recepción de estímulos. En el caso de las ANN las
neuronas serían las que reciben los estímulos que serían los valores generados en las anteriores
neuronas o lo que es igual a nivel técnico valores de entrada o del inglés “Input”.

La principal función del núcleo de la neurona sería el procesamiento, que en una ANN la
principal función equivaldría a realizar las operaciones, por ejemplo, la multiplicación de una
neurona por su peso asociado (lo cual se expondrá más adelante).

Por último, estaría el axón este daría salida a otra neurona o si es la última neurona daría paso,
por ejemplo, al movimiento de un músculo. En el caso de una ANN serían las flechas que asocian
cada neurona con otra. En lenguaje técnico valores de salida o del inglés “Output”.

Figura 2.1.3. Dendrita. (Wikipedia, 2019)

5
Cabe destacar que las redes neuronales artificiales están inspiradas en las biológicas, pero al
ser un modelo formal se obvian muchos procesos biológicos (algunas de las características del
sistema biológico no están reflejados en el modelo computacional y viceversa).

2.1.2 Modelo Matemático.


Una red neuronal artificial se define mediante tres tipos de parámetros distintos:

• Como están conectadas las distintas capas de la red.

• El mecanismo de aprendizaje para que por cada iteración de la red está actualice los
pesos.

• La función de activación de cada neurona que transforma la neurona ponderada en


una activación de salida.

La función de una red neuronal artificial 𝑓(𝑥) es una composición de funciones 𝑔𝑖 (𝑥), la cual
𝑓 (𝑥 ) = 𝐾(∑𝑖 𝑤𝑖 𝑔𝑖 (𝑥 )) donde 𝐾 es la función de activación de la neurona, 𝑔𝑖 (𝑥 ) la neurona 𝑖 y
𝑤𝑖 el peso de la neurona 𝑖. En la siguiente figura se muestra la representación de una neurona
artificial indicando su similitud con el modelo biológico.

Figura. 2.1.4. Símil Red neuronal artificial vs Biológica. (Martínez, Rico, Romero, & Castro, 2018)

Las funciones de activación (𝐾) más usadas en la actualidad son tres:


6
• Sigmoidea (Escolano, 2003): Está función se caracteriza por tener una progresión
temporal desde unos niveles bajos al inicio, hasta acercarse a un clímax transcurrido
un cierto tiempo; la transición se produce en una región caracterizada por una fuerte
aceleración intermedia. La función sigmoide permite describir esta evolución. Su
gráfica tiene una típica forma de "S".

• ReLU (Google, 2019) (del inglés, Rectified Linear Unit): Función de activación
de unidad lineal rectificada, que a menudo funciona mejor que una función suave,
como la sigmoide, y es mucho más fácil de calcular.

• TanH (Wolfram, 2019) (del inglés, Hyperbolic Tangent): Se define como el cociente
entre el seno hiperbólico y el coseno hiperbólico del número real “x”.

En la siguiente figura se puede apreciar las distintas transiciones de estás funciones.

Figura 2.1.5. Funciones de activación (Sigmoid, TanH, ReLU)

Estas funciones se activan en función de los valores de entradas provocando así un cambio leve
en la producción debido a que estas funciones siempre transitan de forma suave.

Por último, el aprendizaje, que es la característica más importante en una ANN. Este es la mejor
optimización para resolver el problema se está tratando. Durante este proceso las neuronas
actualizan su peso 𝑤𝑖 dando lugar a una salida deseada o al menos una consistente, de esta
manera la red podrá en un futuro responder a situaciones aún no procesadas.

Como se ha mencionado anteriormente, este es el proceso más importante, ya que según el


algoritmo que se elija tendrá unas ventajas y unos inconvenientes, pero a su vez si se consigue

7
ajustar bien será capaz de predecir información que viene incompleta dando una salida
coherente.

El aprendizaje se puede englobar en dos categorías, o comúnmente dicho, tiene dos


paradigmas:

1. Aprendizaje Supervisado (Hubel & Wiesel, 2005): En este tipo de aprendizaje, se sabe
cuál es la salida “output” y el mismo consiste en la comparación del resultado que se
obtiene de la red con el teórico esperado. Por consiguiente, se usa una función que
minimiza el error cometido al comparar el valor teórico y el obtenido, y con este error
minimizado, se recalculan los pesos asociadas a cada neurona. Se repite este proceso
hasta que la salida de la red y la esperada son prácticamente iguales. A este tipo de
parejas se le suele denominar conjunto de aprendizaje.

2. Aprendizaje No Supervisado (Geoffrey, 1999): Es un tipo de entrenamiento, muy similar


al biológico. No se utiliza un vector con la salida asociada, si no, que se utiliza solo un
vector de entrada y la red va modificando los pesos de tal manera que las salidas sean
consistentes, es decir que, a entradas muy parecidas, la red compute la misma salida.
Las salidas se asocian a las entradas de acuerdo con el proceso de entrenamiento. Este
proceso extrae características abstrayéndose de las propiedades colectivas subyacentes
del conjunto de entrenamiento, y las agrupa por similitudes.

En cualquier caso, los tipos de aprendizajes más comunes son:

• Aprendizaje Hebbiano: Este tipo de aprendizaje propone que los pesos se


incrementan si la neurona origen y destino están activadas, por lo tanto, premia a
los caminos usados frecuentemente por la red como si fuese un hábito. El
aprendizaje Hebbiano viene caracterizado por la ecuación:

𝑤𝑖𝑗𝑛𝑢𝑒𝑣𝑜 = 𝑤𝑖𝑗𝑣𝑖𝑒𝑗𝑜 + 𝑎𝑘𝑖 𝑏𝑘𝑗

donde i = 1,2, … , n; j = 1,2, … , p; wij es el peso de la conexión entre los dos PEs
(Procesadores Elementales, más comúnmente llamado neuronas).

8
• Aprendizaje Competitivo: Es un tipo de aprendizaje que usa inhibición lateral o lo
que es lo mismo el ganador se lo lleva todo “all-in”. Este tipo de aprendizaje lo usan
redes neuronales como los Mapas Auto-Organizativos (Kohonen, 1984) el cual se
puede observar un ejemplo en la siguiente figura, la cual representa los patrones de
votación de la organización European University Association (del inglés, Asociación
de universidades europeas).

Figura 2.1.6. Mapa auto organizado de los patrones de votación del Congreso de EUA.

(Wikipedia, 2019)

• Aprendizaje Min-Max: Este método se usa para clasificadores. Se utilizan un par de


vectores para cada clase y su función 𝑓(𝑥) viene dada por los vectores 𝑉𝑖 (vector
mínimo) y 𝑊𝑖 (vector máximo). La siguiente ecuación define el aprendizaje Min-
Max:

𝑉𝑖𝑗𝑛𝑢𝑒𝑣𝑜 = min(𝑉𝑖𝑗𝑣𝑖𝑒𝑗𝑜 , 𝑎𝑘𝑖 )

9
𝑊𝑖𝑗𝑛𝑢𝑒𝑣𝑜 = max(𝑊𝑖𝑗𝑣𝑖𝑒𝑗𝑜 , 𝑎𝑘𝑖 )

• Aprendizaje de Corrección de Error: Este tipo de aprendizaje ajusta los pesos de


cada neurona en función de la diferencia entre los valores deseados y los valores
computados. Dependiendo del número de capas de la red se pueden distinguir dos
casos:

a) Red de dos capas: Puede capturar mapeos lineales entre las entradas y las
salidas. Dos redes neuronales que utilizan este aprendizaje son el Perceptrón
(Rosenblatt, 1962) y ADALINE (Widrow & Hoff, 1960).

b) Red multicapa: Puede capturar mapeos no lineales entre las entradas y las
salidas. Este algoritmo es uno de los más usados y comúnmente se le
denomina Backpropagation (Retro propagación). Utilizando una regla
encadenada se pueden recalcular los pesos para un número arbitrario de
capas. Sin embargo, el aprendizaje es lento ya que el número de iteraciones
que se tiene que realizar por cada patrón del conjunto de datos es grande.
Este algoritmo fue introducido por (Rumerlhart, Hinton & Williams, 1986).

• Aprendizaje por Reforzamiento: Ideado por (Widrow, Grupta & Maitra 1973) y
desarrollado por (Willians, 1983). Este tipo de aprendizaje es similar al anterior ya
que es un aprendizaje supervisado y recalcula los pesos por error cometido. Sin
embargo, en este caso el error se calcula por cada neurona de manera individual en
vez de colectivamente como en el caso de las redes multicapa. Este aprendizaje es
bueno en el caso de que se tenga mucha información específica del error. Un
ejemplo del aprendizaje por refuerzo es el de la IA AlphaZero (Deepmind, 2019) de
la empresa DeepMind, la cual, mediante el uso de este aprendizaje ha sido capaz de
derrotar a la mejor IA de Ajedrez hasta el momento (StockFish) (stockfishchess,
2008), a su vez también domina el Go (usgo, 1997) y el Shogi (Sobre Japón), ambos
juegos de estrategias similares al ajedrez. La siguiente figura representa la curva de
aprendizaje del algoritmo AlphaGo zero durante 40 días aprendiendo por sí mismo.

10
Figura 2.1.7. Curva de aprendizaje por refuerzo de AlphaGo. (Deepmind, 2019)

• Aprendizaje Estocástico: Utiliza procesos aleatorios, probabilidad y una relación de


energía para seleccionar los pesos a cambiar y su tamaño en una red neuronal
multicapa. Por lo tanto, aceptan los que mejoran la función y rechazan los malos
según una función probabilística.

En la siguiente tabla se puede observar un pequeño resumen de los distintos aprendizajes vistos
anteriormente. Esta tabla consta de 4 columnas:

1. Algoritmo de entrenamiento.

2. Tiempo que tarda en realizar el entrenamiento al mismo nivel de computación.

3. Categoría a la que pertenece el aprendizaje (no supervisado o supervisado).

4. Capacidad de almacenamiento.

11
Algoritmo de Tiempo de Categoría de Capacidad de
Entrenamiento Entrenamiento Aprendizaje Almacenamiento
Aprendizaje Hebbiano Rápido No supervisado Escaso

Aprendizaje Lento No supervisado Alto


competitivo
Aprendizaje Rápido No supervisado Alto
Min-Max
Aprendizaje Corrección Lento Supervisado Alto
dos niveles
Aprendizaje Corrección Muy Lento Supervisado Muy alto
Multicapa
Aprendizaje Super Lento Supervisado Alto
por Reforzamiento
Aprendizaje Super Lento Supervisado Alto
Estocástico
Tabla 2.1.1. Comparación entre los distintos tipos de aprendizajes en RNN.

2.1.3 Aplicaciones.
Debido a que las redes neuronales tienen características similares a las del cerebro humano,
son capaces de aprender de la experiencia, de abstraer características esenciales a partir de
entradas que presentan información irrelevante, de generalizar de casos anteriores a nuevos
casos…etc. Todo esto permite su aplicación en un gran número de áreas muy diferenciadas.

En el amplio abanico de aplicaciones que hoy en día pueden tener las redes neuronales cabe
destacar algunas en las que se están usando de manera práctica (Matich, 2001):

➢ Procesamiento del habla

o Reconocimiento del habla.

o Pronunciación fonética.

12
➢ Reconocimiento de caracteres y palabras (Lenguaje Natural)

o Resumir texto.

o Crear chat-bots.

o Generar automáticamente etiquetas de palabras clave.

o Reconocer entidades.

o Análisis de sentimiento.

➢ Visión

o Reconocimiento Facial.

o Distinción de objetos, animales, humanos, etc.

o Imagen a texto.

➢ Reconocimiento de Patrones

➢ Robótica

➢ Finanzas

o Previsión de la evolución de los precios.

o Valoración de riesgos en crédito.

o Identificación de falsificaciones.

o Interpretación de firmas.

➢ Medio ambiente

o Analizar tendencias y patrones.

o Previsión del tiempo.

➢ Medicina

o Analizadores del habla para ayudar en la audición de sordos profundos.

o Detección precoz del Cáncer.

13
2.1.3 Software y librerías.
Actualmente, debido al resurgimiento de la IA, muchas de las grandes empresas IT (del inglés,
Information Technology, Tecnologías de la información) han proporcionado gran cantidad de
software, librerías con código abierto (free source) y herramientas para que este ímpetu por
facilitar la vida humana usando IAs para muchísimas cosas no se pierda, y que todo el mundo
pueda aportar su granito de arena. Empresas como el gigante Google (Google, 2019) con
TensorFlow (Tensorflow, 2019) es una biblioteca de código abierto para aprendizaje
automático a través de un rango de tareas. Desarrollado por Google para satisfacer sus
necesidades de sistemas capaces de construir, entrenar redes neuronales , detectar y descifrar
patrones, correlaciones, análogos al aprendizaje y razonamiento usados por los humanos.
TensorFlow fue originalmente desarrollado por el equipo de Google Brain para uso interno en
Google, antes de ser publicado bajo la licencia de código abierto Apache 2.0 el 9 de noviembre
de 2015. TensorFlow, está hecho en C++ (Stroustrup, 1985), aunque, su uso habitual, para que
sea más rápido, cómodo y sencillo de usar es mediante el uso de la API (del inglés, application
programming interface, Interfaz de programación de aplicaciones) en Python (Python, 2019). El
Playground (Tensorflow, 2019) de TensorFlow es una herramienta destinada a que se conozca
cómo funciona por dentro una red neuronal de manera gráfica e interactiva. En la siguiente
figura se aprecian las distintas compañías que usan TensorFlow.

Figura 2.1.8. Principales empresas que utilizan TensorFlow para machine learning. (Tensorflow, 2019)

14
Facebook (Facebook, 2019), desarrolló Torch (Torch, 2019) una biblioteca de código
abierto para aprendizaje automático, un marco de computación científica, y un lenguaje
de script basado en el lenguaje de programación Lua (Lua, 2019). Proporciona un amplio rango
de algoritmos de aprendizaje profundo, y usa el lenguaje de script LuaJIT (Lua, 2019),
existiendo, también, una implementación inferior en C (Brian & Dennis, 1978).

No podía faltar la compañía, Microsoft (Microsoft, 2019), con su The Microsoft Cognitive Toolkit

(CNTK) (Microsoft, 2019), que es un conjunto de herramientas software de código abierto y

una de las primeras en proporcionar acceso al deep learning (vertiente del machine learning

que usa técnicas de aprendizaje más detalladas) en Open Source. Esta, combina los principales

modelos de aprendizaje profundo y machine learning, optimizadores del error mediante el uso

del Gradient Descent (del español, descenso por gradiente), el cual, es un método matemático

general de minimización para cualquier función y el backpropagation que, a su vez permite el

uso simultaneo de múltiples

GPUs y CPUs. La librería se puede acceder mediante el uso de lenguajes de programación,

Python, C++ y C# (C sharp) (Microsoft, 2019).

En la universidad de Berkeley (BAIR, 2019) se desarrolla Caffe (Berkeley AI Research, 2019), es


un framework (del español, entorno de desarrollo) hecho para hacer deep learning con claridad,
velocidad y para ser modular.

Theano (Theano, 2019), fue un proyecto open source de la Universidad de Montreal (Université
de Montréal, 2019), en el cual usando una de las librerías más famosa de Python (Numpy
(Numpy, 2019)) consiguieron desarrollar una librería en Python ultra escalable, tanto en GPUs
como en CPUs.

Por último, Keras (Keras, 2019), es un framework de código abierto para redes neuronales
escrito en Python y es capaz de usar varios de los frameworks anteriormente vistos como
TensorFlow, CNTK, Theano, y, además, MXNet (Mxnet, 2019). Añade un nivel, más de
abstracción permitiendo así un uso particularmente fácil para alguien que no sepa programar

15
prácticamente. Fue desarrollado en un proyecto llamado ONEIROS (Open-ended Neuro-
Electronic Intelligent Robot Operating System) y su principal autor es François Cholle (Keras,
2019) un ingeniero de Google. En la siguiente imagen se puede observar el uso para deep
learning de las distintas librerías y cuáles son las preferidas a nivel de puntuación en estrellas
de Github (un sistema de control de versiones bastante popular) (GitHub, 2019).

Figura 2.1.9. Deep learning frameworks con más popularidad en GitHub (GitHub, 2019).

2.2 FPGAs.
Una FPGA (Field Programming Gate Array, matriz de puertas programables) (University of
Toronto, 2019) es un dispositivo lógico programable que contiene un conjunto de CPLDs (del
inglés, Complex Programmable Logic Device, dispositivo lógico programable) para los cuales su
funcionalidad e interconexión se realiza mediante el uso de un lenguaje de descripción
hardware como puede ser VHDL (IEEE, 1988) el cual se detallará más adelante. A continuación,
se muestra un ejemplo de FPGA de la gama virtex UltraScale de xilinx.

16
Figura 2.2.1. FPGA de la empresa XILINX, gama Virtex Ultra Scale. (Xilinx, 2019)

Las FPGAs nacen debido a que su principal diferencia frente a los ASICs (del inglés, Application-
Specific Integrated Circuit, circuito integrado de aplicación específica), en que son
reprogramables, por lo tanto, añaden reducciones de coste y diseño. Sin embargo, es verdad
que todo tiene sus inconvenientes cuando se comparan cosas, y en el caso de la FPGA se ve
afectado en que el rendimiento y consumo energético son mayores con respecto a los ASICs.

Teniendo en cuenta lo anteriormente mencionado, una FPGA es un conjunto de CPLDs,


entonces, alguien se podría preguntar ¿por qué una FPGA? Simplemente es una evolución de
los CPLDs, y si un CPLD contiene miles de bloques lógicos programables, las FPGAs contiene un
gran número de CPLDs.

Las FPGAs fueron desarrolladas en el año 1984 por Ross Freeman y Bernard Vonderschmitt
(Funding Universe, 2019), cofundadores de Xilinx (Xilinx, 2019), y surgen como, ya se ha
explicado anteriormente, una evolución de los CPLDs.

Aparte de la diferencia de densidad de ambos dispositivos, otra de las diferencias principales es


a nivel arquitectural, como se muestra en la figura, un CPLD es más rígido. Las operaciones que

17
realizan los bloques lógicos van a parar a unos escasos biestables síncronos, mientras que, en
una FPGA cada bloque lógico contiene dichos biestables, por lo que, da mayor libertad.

Por otro lado, dentro de la matriz de conexionado, en una FPGA se le pueden añadir puertas
lógicas que facilitan el cálculo de operaciones matriciales, como puede ser sumadores o
multiplicadores.

Figura 2.2.2. Conexionado de una FPGA. (Elijah Charles, 2019)

Una vez explicado un poco de historia y comparado con sus dispositivos similares más próximos,
se pueden preguntar ¿para qué se va a usar un elemento hardware tan desconocido si existen
hardware genérico como las CPUs y GPUs? Pues, bien, precisamente se quiere abordar este
tema ya que es una de las preguntas que más se hacen los desarrolladores cuando se enfrentan
a una decisión de elección de tecnologías a usar para realizar las implementaciones hardware
de sus sistemas.

2.2.1 ¿Por qué una FPGA?


Cuando un Ingeniero/a se enfrenta a una toma de decisión como puede ser necesidad de
capacidad de computo por consumo eléctrico, ya que, no se dispone de presupuesto suficiente
para costear un clúster de GPUs/CPUs o simplemente se quiere que a la larga la solución tomada
salga más económica, en este momento es cuando resaltaría el uso de las FPGAs ¿Por qué?
Porque en comparación a las GPUs (su rival en computación más próximo sobre todo las que

18
constan de operaciones matriciales) el procesamiento por vatio de una FGPA es unas 3-4 veces
mejor que el de una GPU (GFLOPs por vatio).

Por consiguiente, cuando se trate de un proyecto en el que se piense su implementación


mediante el uso de GPUs como puede ser para proyectos del tipo:

• Cloud Computing

• Procesamiento de Imagen

• Big Data

• Ancho de banda

• Robótica

• HD-Video

• machine learning

• deep learning

Por otro lado, se tendrá que tener en cuenta otros requisitos existentes tales como,
presupuesto, compatibilidades, es un sistema de tiempo real, espacio, desarrollo, flexibilidad,
tipos de interfaces, cálculo y latencia. Para ello, a continuación, se describirá una tabla
comparativa en donde se muestra a modo resumen en cuales de los aspectos descritos
anteriormente es mejor una FPGA o una GPU.

Características Análisis Ganador


Procesamiento de El total de operaciones en coma flotante por segundo
operaciones en coma de la mejor GPU es más alto que el de la mejor FPGA.
flotante
GPU
Cuando se implementa un algoritmo en una FPGA se
producen un tiempo determinista por lo tanto a este
tiempo es menor que en una GPU.

Latencia FPGA

19
Características Análisis Ganador
Una FPGA gana en GFLOPs por vatio en ratio de 2-3 a
una GPU.
Procesamiento por FPGA
vatio
Las GPUs solo pueden estar conectadas vía PCIe
mientras que las FPGAS pueden estar por PCIe, fibra
óptica, en sistemas empotrados, como
coprocesadores, puertos serie, etc.
Interfaces FPGA

Los desarrollos para una GPU antigua funcionarán en


una nueva, sin embargo, en una FPGA habrá que
modificar un poco de código.
Compatibilidad con GPU
dispositivos anteriores
Las FPGA carecen de flexibilidad para modificar la
implementación de un código sintetizado algo que en
Flexibilidad GPU
las GPUs no sucede.

Las FPGAs requieren menos consumo, menos


disipación térmica, por lo que se pueden implementar
Tamaño FPGA
en dimensiones más pequeñas.
Muchísimos desarrollos están implementados en
GPUs, sin embargo, el coste de contratar un
desarrollador hardware y las soluciones más difíciles
de implementar provocan que sea más caro.
Desarrollo GPU
Las gamas medias de FPGAs pueden compararse con
las mismas en GPUs pero cuando se sube a una gama
alta el coste es mucho mayor en las FPGAs.
Procesamiento/€ GPU
Tabla 2.2.1. GPU vs FPGA comparación de rendimiento

Como se puede ver, hay bastantes aspectos a tener en cuenta a la hora de elegir entre uno u
otro. En la siguiente figura se puede ver la tabla en un gráfico cualitativo.

20
Figura 2.2.3. Gráfico Cualitativo de la tabla 2.2.1. (Berten, 2019)

2.2.2 Arquitectura de una FPGA.


Cada chip de FPGA está hecho de un número determinado de recursos predefinidos con
interconexiones programables para implementar un circuito digital reconfigurable y bloques de
E/S para permitir que los circuitos tengan acceso al mundo exterior. En la siguiente figura se
puede observar las partes principales de una FPGA.

Figura 2.2.4. Diferentes partes de una FPGA. (National Instruments, 2019)

Los bloques lógicos o CLB constan de tres elementos principales (como se puede observar en la
figura 2.2.5), las LUTs (Look Up Table), un full-adder (del inglés, sumador total) y un flip-flop (del
21
inglés, biestable) tipo D. Con este conjunto mediante, LUTs, las cuales, usan multiplexores
dentro para generar cualquier circuito combinacional, como se ejemplifica en la figura 2.2.6,
además de otro (multiplexor) para seleccionar si el modo es asíncrono o síncrono. A
continuación, pasa por el full-adder, este se usa para interconectar varias LUTs con el propósito
de poder realizar circuitos combinacionales más complejos y el resultado generado se guarda
en el biestable D para así guardar la configuración del bloque lógico (esto da la opción de que
sea reprogramable).

Figura 2.2.5. Componentes de un bloque lógico. (Wikipedia, 2019)

Figura 2.2.6.
Como se genera un circuito combinacional a través de una LUT. (Blanco Pinto, 2019)

22
En esta figura, se describe como hacer un circuito combinacional en una LUT, para ello se
dispone de una memoria SRAM, y, de un multiplexor n-1. El número de entradas definirá el
tamaño del multiplexor, en este caso son tres entradas 23 = 8, por lo que, el multiplexor será
de ocho a uno. Una vez visto los componentes, se procede a realizar la tabla de verdad del
circuito a implementar. Por consiguiente, las distintas combinaciones de la salida “y” se
introducen en los distintos bloques de la SRAM de manera ordenada, haciendo coincidir las
entradas del multiplexor con el bloque de memoria correspondiente a la combinación para
activar la entrada del multiplexor.

Por otro lado, la matriz de Inter-conexionado es la que genera la conexión entre los distintos
bloques lógicos. Está matriz, aunque pase por todos los bloques no tiene por qué estar activada
ya que el enrutamiento de una FPGA no está segmentado, o sea, cada segmento de cableado
abarca solo un bloque lógico y está termina en un switch (como se observa en la siguiente
figura), el cual, se pueden hacer caminos más largos activando los switch y haciendo así la
conexión entre estos CLB.

Figura 2.2.7. Switch de unión entre los distintos CLB de la matriz de conexionado. (Wikipedia, 2019)

Los bloques de E/S (I/O) son básicamente por donde se hará la interacción con la FPGA tanto
en entrada de datos como de salida de los mismos. En la figura 2.2.8 se puede apreciar el
circuito I/O, que someramente se puede indicar que es un circuito estándar de entrada y salida
de datos.

Por último, a parte de estos tres principales bloques las FPGAs, por lo general llevan integrados,
bloques de RAM y DSPs (del inglés, Digital Signal Processor, Procesadores Digitales de Señales),
23
los cuales tienen muchas funcionalidades, pero la principal o por lo menos una de las cuales
afectará mucho a este PFG es el uso de los DSPs para hacer multiplicadores-acumuladores,
matrices y operaciones en coma flotante.

Figura 2.2.8. Estructura de I/O Xilinx Spartan. (Xilinx, 2019)

2.2.3 FPGAS en el mercado.


En la actualidad existen numerosas empresas las cuales se dedican a la creación y distribución
de FPGAs, aunque la primera fue Xilinx (Xilinx, 2019), existen otras como Intel (Intel, 2019),
Atmel (Atmel), AMD (AMD, 2019) y Motorola (Motorola, 2019).

24
En este proyecto se usará y se comparan las distintas gamas de FPGAs de la empresa Xilinx
(Figura 2.2.9). La elección fue debido a que en el BBVA usaban la gama alta de FPGAs de Xilinx
e, incluso, tienen un programa de ayuda a la educación, donde, proporcionan FPGAs de prueba
para educación. También, ofrecen la licencia para que se use un software, el cual, proporciona
herramientas y útiles para poder modelar la FPGA de manera más simple. En la siguiente figura
se muestra el logotipo de uno de los principales fabricantes de FPGAs actualmente, Xilinx.

Figura 2.2.9. Logo de Xilinx. (Xilinx, 2019)

Dentro de Xilinx existen varias gamas de FPGAs, como se puede ver en la tabla 2.2.2,
mostrándose, de izquierda a derecha según desciende el tamaño de fabricación de los
transitores la gama a la que pertenecen las mismas, empezando por la gama con mayor tamaño
de transistores, a la izquierda. Dentro de la misma tecnología, se mejoran las prestaciones de
más abajo (la que menos prestaciones tienes) a más arriba (la que más), por ejemplo, se pueden
usar con fibra óptica, tienen más bloques de RAM, DSP, LUTs, I/O, etc. Lo que provoca, que se
puedan hacer proyectos más grandes o que requieran de mayor complejidad, flexibilidad,
capacidad de memoria principal o I/O.

Tabla 2.2.2. Gamas de FPGAs de Xilinx. (Xilinx, 2019)


25
Por ejemplo, para que se pueda observar la diferencia entre una gama ultra scale (columna de
la derecha de la tabla anterior) y la versión “estándar” (columna de la derecha de la tabla
anterior), xilinx proporciona una tabla comparativa (tabla 2.2.3) en la que se observan las
mejoras de las prestaciones descritas anteriormente.

Tabla 2.2.3. Comparativa entre gama Kintex-7 y Kintex Ultra Scale. (Xilinx, 2019)

Para este tipo de aplicación, Big Data, se podría usar una de las gamas más altas y
probablemente usar varías si se quiere para uso empresarial, ya que, se precisa de memoria y
mucha E/S debido a la cantidad de datos que las redes neuronales tratan. Para que se entienda
esto último, si en la primera capa de la red neuronal se tienen 3 neuronas y números de entrada
en coma flotante (264 ) como estos buses de datos serían en paralelo, se usarían 64 x 3 = 192
bloques de E/S, teniendo en cuenta que el ejemplo es muy básico para una red neuronal, y, que
una gama alta dispone de unos 500 bloques de E/S se usarían el 40% de los bloques de E/S. Sin
embargo, para hacerlo con pocos datos y a pequeña escala como es el objetivo de este
proyecto, vale con la simulación que genera Vivado (Xilinx, 2019), software de Xilinx para
modelar FPGAs, y una FPGA de gama baja o media.

26
Para hacerse una idea, en la tabla 2.2.4, la cual esta sacada de las especificaciones de Xilinx de
la gama Virtex UltraScale+, se puede observar la cantidad de principales prestaciones que tiene
esta gama (LUTs, RAM, DSP, I/O, CLK). En los resultados del proyecto se adjuntará una tabla
similar indicando la cantidad de LUTs, RAM, DSPs, E/S y CLKs usados, y, que recursos se
necesitarían para un proyecto más grande.

Tabla 2.2.4. Resumen de características de la FPGA Virtex ultrascale+ de Xilinx. (Xilinx, 2019)

2.2.4 Aplicaciones. (Xilinx, 2019)


A continuación, se muestra una lista, elaborada por el fabricante Xilinx, de proyecto tipos
donde se han implementado, según el mismo, soluciones en FPGAs:

1. Defensa y Aeroespacial

• Avionics/DO-254

• Comunicaciones

• Misiles y municiones

• Soluciones de seguridad

27
• Espacio

2. Audio

• Conectividad

• SDR o Radio definida por software

• DSP

• Reconocimiento del lenguaje

3. Industria Automotriz

• Alta resolución de video

• Procesamiento de imagen

• Conectividad y redes de vehículos

4. Bioinformática

5. Broadcast

• Motor de reproducción de video en tiempo real

• Codificadores

• Switches y Routers

• Monitores

• EdgeQAM (Video On Demand)

6. Electrónica de consumo

• Monitores digitales

• Cámaras digitales

• Impresoras Multifunción

• Dispositivos portables

• Decodificadores

28
7. Centro de Datos

• Servers (servidores)

• Seguridad

• Módulo de seguridad Hardware

• Routers

• Switches

• Gateways

• Balanceadores de carga

8. Super computación

• Servers

• Super Computadores

• SIGINT sistemas

• Radares

• Sistemas de minería de datos

9. Industrial

• Redes industriales

• Control de Motores

10. Circuitos embebidos

• ASICs

• Emulación hardware

11. Medicina

• Ultrasonidos

• Resonancias magnéticas

29
• Rayos-X

• Tomografías

• Sistemas quirúrgicos

12. Seguridad

• Soluciones de seguridad

• Módulo de seguridad hardware

• Procesamiento de Imagen

13. Procesamiento de imagen y video

• Video de alta resolución

• Video sobre el protocolo IP

• Monitores digitales

• Visión artificial

14. Comunicaciones por cable

• Fibra Óptica (Multiplexación)

• Procesamiento de red

• Interfaces de conexión

15. Comunicación Wireless

• Backhaul

• Radio

• Banda base

Como se puede observar las FPGAs son aplicables a una gran parte de los proyectos que
necesita la sociedad, desde aplicaciones para economía hasta medicina. Esto es debido a que al
ser un dispositivo hardware “genérico” se pueden usar para muchísimas aplicaciones y es
reprogramable a su vez, modelándolo de una cierta manera con un lenguaje de modelación
30
hardware como puede ser VHDL, esta se convierte en un dispositivo hardware especifico.
Debido a este motivo, las FPGAs proporcionan otros casos de uso, en el cual, sus “hermanos”
genéricos (CPU, GPU) tienen más dificultades para hacer frente a este tipo de situaciones.

2.3 VHDL.
VHDL (Wikipedia, 2019) es un lenguaje de especificación definido por el IEEE (IEEE,
2019) (Institute of Electrical and Electronics Engineers, Instituto de ingeniería eléctrica y
electrónica) (ANSI/IEEE 1076-1993) (IEEE, 1988) utilizado para describir circuitos digitales y para
la automatización de diseño electrónico. VHDL es acrónimo proveniente de la combinación de
dos acrónimos: VHSIC (Very High Speed Integrated Circuit, circuito integrado de muy alta
velocidad) y HDL (Hardware Description Language, lenguaje de descripción hardware). Aunque
puede ser usado de forma general para describir cualquier circuito digital, se usa principalmente

Para programar PLDs (Programable Logic Devices -Dispositivos Lógico


Programable), FPGAs (Field Programmable Gate Arrays), ASIC y similares.

Una vez vista la definición del lenguaje VHDL, se revisará, someramente, su historia. VHDL se
creó a la vez que ADA por el Departamento de Defensa (DOD, Department of Defense) (DOD,
2019) de los Estados Unidos con la idea de documentar todos los ASICs que sus sistemas de
defensa llevaban y los entendiese todo el mundo o, lo que es lo mismo, un estándar. Como se
ha expuesto anteriormente, VHDL es coetáneo de ADA, aunque, por motivos lógicos es
diferente a ADA, si bien, este tiene gran influencia en la sintaxis de VHDL.

Al principio de su creación, en la fase inicial de VHDL (1987), el IEEE incluyó algunos tipos de
datos como pueden ser, numéricos (enteros y reales), lógicos como el bit y el boolean,
caracteres, tiempo, y un vector de bits (bit_vector), para, posteriormente, comprobar que les
faltaba un tipo de datos para hacer una lógica plurivalente, por lo que posteriormente
programaron el tipo de dato std_logic_vector en el cual se podían ya definir un bus de datos.
Ya hasta 2002 no hubo cambios significativos, solamente opciones de seguridad como los tipos
de datos protegidos y añadieron complejidad a los tipos de datos reales con los “unsigned” (sin
signo) y “signed” (con signo).

31
2.3.1 Diseño y Estructura.
En VHDL hay varias formas de diseñar un mismo circuito, de un nivel de abstracción mayor a
uno menor, todas ellas son igualmente válidas, siendo elección de cual usar de las mismas del
desarrollador, teniendo cada una sus procedimientos de diseño específicos:

• Funcional: Esta es la forma de diseño más parecido a la programación software, es


a nivel algorítmico. Este diseño solo tiene en cuenta la relación entre las entradas y
las salidas sin especificarse ni la estructura ni la implementación física, mediante el
uso de la estructura if-then-else, por citar la más básica.

Ejemplo de diseño funcional:

1. architecture functional of neuron is


2.
3. signal acc : signed (NbitIn+NbitW+Nbflow downto 0) := (others => '
0'); -- Accumulator register
4. signal Mreg : signed (NbitIn+NbitW-
1 downto 0) := (others => '0'); -- Multiplier output register
5.
6. begin
7.
8. process (CLK)
9. begin
10. if CLK'event and CLK = '1' then
11. if RST = '1' then
12. acc <= (others => '0');
13. Mreg <= (others => '0');
14. else
15. if CE1 = '1' then
16. Mreg <= signed(A)*signed(B);
17. end if;
18. if CE2 = '1' then
19. if LOAD = '1' then
20. if dirload then
21. -- Load acc with port C value (bias):
22. acc <= resize(signed(C),NbitIn+NbitW+Nbflow
+1); -- Sign extension
23. else
24. -- Load acc with A*B + C (bias):
25. acc <= resize(signed(C),NbitIn+NbitW+Nbflow
+1) + Mreg;
26. end if;
27. else
28. acc <= acc + Mreg;
29. end if;
30. end if;
31. end if;
32
32. end if;
33. end process;
34.
35. P <= std_logic_vector(acc);
36.
37. end functional;

• Flujo de Datos: En este diseño, se describe la forma en la que los datos se trasfieren
entre los distintos módulos de la entidad mediante la estructura when-else.

Ejemplo de diseño flujo de datos:

1. entity Sumador2bits is
2. Port ( c_in:in STD_LOGIC; --Carry de entrada
3. sum_0 : in STD_LOGIC; --Bit a
4. sum_1 : in STD_LOGIC; --Bit b
5. total : out STD_LOGIC; --Resultado a+b
6. c_out:out STD_LOGIC ); -- Carry de salida
7. end Sumador2bits;
8.
9. architecture flujo of Sumador2bits is
10. begin
11. total<= (sum_0 xor sum_1 xor c_in); --a+b+carry in
12. c_out<= (sum_0 and sum_1) or((sum_0 xor sum_1)and c_in);
--Carry out
13. end flujo;

• Diseño Estructural: En este caso se modela el circuito con modelos lógicos hechos
por el fabricante o por el usuario, como pueden ser puertas lógicas, sumadores,
contadores, etc.

Ejemplo de diseño estructural:

1. library IEEE;
2. use IEEE.STD_LOGIC_1164.ALL;
3.
4. entity medio_sumador is
5. port(
6. A : in STD_LOGIC;
7. B : in STD_LOGIC;
8. C : out STD_LOGIC;
9. S : out STD_LOGIC
10. );
11. end medio_sumador;
12.
13. architecture estructural of medio_sumador is

33
14. component compuerta_and is
15. port(
16. a : in std_logic;
17. b : in std_logic;
18. s : out std_logic
19. );
20. end component;
21.
22. component compuerta_xor is
23. port(
24. a : in std_logic;
25. b : in std_logic;
26. s : out std_logic
27. );
28. end component;
29.
30. begin
31.
32. ACARREO: compuerta_and port map( A, B, C );
33. SUMA: compuerta_xor port map( A, B, S );
34.
35. end estructural;

• Por último, el diseño mixto el cual consiste en una combinación de los anteriores de
cualquier manera.

A continuación, se muestra la metodología de diseño, la cual es muy metódica, teniendo como


consecuencia que una vez finalizada toda la estructura entera es muy improbable que el sistema
no funcione conforme se le haya modelado, ya que, con este método se asegura definir, diseñar
(conforme a los requisitos de la definición), QA (Quality Assurance), o lo que es lo mismo,
comprobando que los requisitos se cumplen, y por último, que la descripción hardware
realizada, sea válida para todo tipo de FPGAs. Son nueve pasos:

1. División del diseño en módulos separados, como se observa en la figura 2.3.1. Debido a
esta modularidad se obtiene que en un futuro se pueda usar este módulo para otros
proyectos o ampliar el mismo sin necesidad de cambiar prácticamente nada.

34
Figura 2.3.1. Ejemplo de división por módulos.

2. Planificar el tipo de diseño de cada módulo como se vio anteriormente y desarrollarlo.

3. Simulación funcional. En este apartado se comprobará que el punto anterior funciona


correctamente y como se ha planteado. Sino se volverá a la primera etapa, la de diseño.

4. Síntesis. Una vez que se verifica la simulación funcional se procederá a hacer la síntesis
en la cual, se asigna un hardware en concreto (FPGA, ASIC) y se comprueban si se ha
incumplido alguna operación que no es sintetizable, como puede ser la división o
exponenciación de números no constantes.

5. Simulación Post-Síntesis, en este paso se comprobará que el sintetizador ha


transformado el código HDL en bloques lógicos conectados entre sí que funcionan
correctamente.

6. Ubicación y enrutamiento (Place and Route, P&R), no siendo necesario, aunque es muy
útil ya que en este procedimiento es donde se enrutan los bloques y pueden provocar
retardos de propagación si un bloque que está muy conectado con otro bloque y, este,
está muy lejos del mismo (pudiendo ser recolocado).

7. Una vez hecho el enrutamiento, se asignan las latencias a cada bloque SDF (del inglés,
Standard Delay Format, Formato estándar de retardos) dando paso a la siguiente etapa
que será la simulación temporal.

35
8. Simulación temporal. En este apartado, se comprobará lo mismo que en la simulación
funcional, pero con los correspondientes retardos lo que puede provocar incoherencias
en el resultado teniendo que volver a una de las etapas anteriores ya que se tendrá que
optimizar el enrutamiento o solventar una condición de carrera, etc.

9. Por último, una vez que se valida la simulación temporal se volcará el código en el
dispositivo en cuestión, y se comprobará que todo funciona correctamente.

2.3.2 Sintaxis.
Para empezar en la figura 2.3.2 se observa una lista de todas las palabras reservadas en VHDL.

Figura 2.3.2. Palabras reservadas VHDL. (Mancebo, 2019)

Ahora, teniendo una referencia de las palabras reservadas, en VHDL existen dos divisiones muy
claras con respecto a la estructura. Una es la parte declarativa donde se define simplemente la
entidad con sus respectivas, entradas, salidas y el tipo de estas, y, otra totalmente aparte donde
se define su arquitectura detalladamente y el comportamiento de la entidad.

Por lo tanto, se tiene la entidad que se define mediante la palabra reservada “entity” y la
arquitectura mediante “architecture”.
36
Ejemplo declarativo de entidad:

1. ENTITY Nombre_entidad IS
2. PORT ( Nombre de señal: modo tipo de señal;
3. . . .
4. Nombre de señal: modo tipo de señal );
5. END nombre_entidad;

Ejemplo declarativo de arquitectura:

1. ARCHITECTURE nombre_arquitectura OF nombre_entidad IS


2. Declaración de tipos
3. Declaración de señales.
4. Declaración de constantes
5. Declaración de componentes
6. Definición de funciones
7. Definición de procedimientos
8.
9. BEGIN
10. Bloque lógico
11. END nombre_arquitectura;

Por otra parte, se tienen los operadores:

• Operadores Lógicos: AND, XOR, OR, NOT, NOR, NAND, XNOR, para BIT y BOOLEAN.

• De relación: = (igual), /= (distinto), < (menor que), > (mayor que), >= (mayor igual),
<= (menor igual) para enteros.

• Aritméticos: +, -, *, /, Rem (Resto de la división), Mod (Módulo de la división), Abs


(Valor absoluto), **(Entero elevado a una constante).

Otras dos de las palabras reservadas muy importantes son “component” y “package” cuando
se define un componente se crea una instanciación concurrente de una entidad previamente
diseñada la cual se suele usar en una instancia de nivel superior, por ejemplo, si quieres hacer
un acumulador se necesita un registro tipo D y un sumador de n bits, la instancia superior sería
el acumulador y esta tendría dos componentes el registro tipo D y el sumador de n bits. La
declaración de los componentes tiene que ser en la parte declarativa de una arquitectura.

Pero claro, como bien se ha dicho otra de las palabras reservadas asociada a esta es “package”
donde, por ejemplo, se definirá los componentes, funciones, procedimientos, señales y
constantes que irán en ese paquete. El “paquete” se podrá usar en cualquier proyecto

37
importando dicho “paquete”, sin embargo, los componentes solo se pueden usar en la misma
raíz del proyecto.

Un ejemplo declarativo de un componente:

1. architecture Functional of top-level is


2.
3. component neuron is
4. generic
5. (
6.
7. nbits : natural --- Bit width of the input data
8. );
9. port
10. (
11. CLK : in std_logic;
12. RST : in std_logic;
13. A : in std_logic_vector (nbits-1 downto 0); -- Input
data
14. B : in std_logic_vector (nbits-1 downto 0); --
Weights
15. P : out std_logic_vector ((nbits*4)-1 downto 0); --
Output data
16. CE1 : in std_logic; -- Multi enable
17. CE2 : in std_logic; -- Accumulator enable
18. LOAD : in std_logic -- Load signal. Resets the
accumulator with value determined by dirload parameter
19. );
20. end component;

Un ejemplo declarativo de paquete:

1. PACKAGE nombre_paquete IS
2. Declaración de tipos
3. Declaración de señales.
4. Declaración de constantes
5. Declaración de componentes
6. Definición de funciones
7. Definición de procedimientos
8. END nombre_paquete
9.
10. PACKAGE BODY nombre_paquete IS
11. Declaración de tipos
12. Declaración de constantes
13. Definición de funciones
14. Definición de procedimientos
15. END nombre_paquete

38
2.3.3 Vivado Xilinx.
Vivado Design Suite (cuyo logo se muestra en la Figura 2.3.3) es un software de Xilinx escrito en
C++ para la síntesis y el análisis de proyectos HDL o de modelación hardware siendo,
básicamente, lo mismo que el ISE de Xilinx pero con alguna característica más y una mejor
experiencia de usuario en cuanto a usabilidad. Es el entorno de desarrollo que se usará en este
proyecto ya que proporcionará herramientas de diseño, un editor para VHDL, simulaciones
funcionales, temporales, librerías con componentes hechos por Xilinx (algunas son de pago
otras no). Por último, también permite la realización de la síntesis del HDL a parte de otras
utilidades, como importación de módulos ya hechas.

Figura 2.3.3. Logo de vivado. (Doulos, 2019)

2.3.4 Otros lenguajes HDL.


El competidor más próximo de VHDL, es Verilog (Verilog, 2019), y aunque existen muchas más
variantes de lenguajes HDL, los más comunes y con más soporte son VHDL y Verilog. La decisión
de escoger entre uno u otro es debido a que en VHDL, se disponía de conocimientos previos
adquiridos en la asignatura de Integración de Sistemas de la Universidad Politécnica de Madrid.
A su vez, se buscó información acerca de si existía alguna diferencia de rendimiento entre
ambos, pero no se encontró ninguna. Sin embargo, si existen diferencias como:

1. VHDL es un lenguaje fuertemente tipado, Verilog débilmente tipado.

2. VHDL es muy determinista, en cambio, Verilog solo lo es cuando se sigue ciertas reglas.

3. VHDL, en lo que a sintaxis se refiere, es parecido a ADA. Sin embargo, Verilog, es más
parecido a C.

4. VHDL se usa más en Europa, y, Verilog en EEUU.

39
40
Capítulo 3: Desarrollo
En este capítulo se expondrá como se ha realizado el proyecto, las distintas tomas de decisiones,
razones por las que se han tomado esas decisiones y por último su implementación en VHDL
con su respectiva arquitectura, diseño, etc.

3.1 Arquitectura
En primer lugar, a lo largo de este apartado se irá describiendo la arquitectura a través de una
figura de una red neuronal (como se observa en la figura 3.1.1), se irá focalizando por partes
realizando así una arquitectura estructural por bloques de menor a mayor (arquitectura
comúnmente llamada bottom-to-top). Antes de nada, aclarar que en este apartado no se verá
la implementación a fondo ya que esto se hará en el apartado de implementación en VHDL.

Figura 3.1.1. Arquitectura Red Neuronal.

1. La entidad más pequeña de una red neuronal, quitando las entradas y las salidas que no
dejan de ser a nivel hardware señales, es la neurona, como se expuso anteriormente,
siendo la unidad básica de computo de una red neuronal, por lo tanto, la que se va a
encargar de hacer con unos datos que le lleguen una serie de operaciones.

41
Para ello, se realizará una entidad que se denominará “neuron” y será la unidad de
procesamiento que constará simplemente de un acumulador y un multiplicador con una
serie de “flags” para saber cuándo acumular, resetear, multiplicar, etc. Por consiguiente,
habrá otros dos módulos, uno encargado de controlar todo este componente y el otro
será el proceso de acceder a la función sigmoid que se tratará más adelante. Con
controlar, se refiere al hecho de activar o desactivar las señales de control que harán
que esta neurona, acumule o multiplique en función de lo que se requiera en ese
momento y el resultado pasarlo por el proceso de lectura de la función sigmoid. Por lo
tanto, se deberá tener una entidad top que será la que contenga estas tres entidades
como se puede apreciar en la figura 3.1.2 con sus respectivas entradas y salidas.

Figura 3.1.2. Entidad top de una neurona.

Una vez visto la neurona se podría decir que la entidad Neuron_top es igual a una
neurona de la figura 3.1.1 que se veían representadas como a1, a2, a3 … an.

2. La siguiente entidad es el Layer (figura 3.1.3) o capa, la cual crea y controla neuronas,
esta contiene a Neuron_top y simplemente constará de una unidad de control (no hará
ningún cálculo), generará las neuronas necesarias para esa capa y tendrá asociada una
matriz con los pesos de cada neurona. Esta matriz será de NxM donde N será el número
de neuronas de la capa anterior y M el número de neuronas de la capa actual. Recordar
42
que los pesos estarán en memoria fijos (no se recalcularán) ya que el objetivo de este
proyecto es la viabilidad de una red neuronal en una FPGA, por lo tanto, no es necesario
añadirle más complejidad de algoritmia (Backpropagation para recalcular los pesos
asociados). La parte de control de la capa es muy básica y simplemente se irá
comunicando con las neuronas proporcionándoles los pesos asociados a cada una y una
serie de activadores de señal. En la siguiente figura se muestra a nivel arquitectural la
entidad descrita previamente, con sus componentes, entradas y salidas.

Figura 3.1.3. Entidad Layer.

3. Una vez realizada la entidad de layer_generation, se procederá a definir la función de


activación. La función de activación que se ha llevado a cabo en este proyecto es la
función sigmoid (figura 3.1.4). La función sigmoid básicamente proporcionará un
43
resultado en función del valor de entrada, indicando el valor con el que se activará la
neurona. Esta entidad constará de una función que dependiendo del número de bits de
salida de la entidad neuron, creará una LUT con todas las combinaciones de entradas
del bus de datos, consiguiendo así una discretización de la función como se muestra en
la figura 3.1.5. Esta LUT se creará una vez se sintetice el proyecto. Por otra parte, tendrá
un proceso en el que dado un flag de activación y un dato de entrada, se consultará esta
LUT con el valor de entrada y devolverá el valor previamente calculado del mismo modo
que una indexación (DATAprix, 2019) en una base de datos. Para construir la función
que crea esta LUT se encontraron varias dificultades ya que esta función ( 𝑓 (𝑥 ) =
1
) usa un valor constante elevado a uno variable, esta operación no es sintetizable
1+𝑒 𝑥
en VHDL y, por lo tanto, no se puede realizar. Debido a esto, se decidió discretizar la
función (figura 3.1.5). La manera en la que se implementó es haciendo una serie de
“escalones” muy cercanos a la función, entonces, según el número de bits de entrada
(cuanto más sea el número de bits menor error en la aproximación se produce, ya que,
los intervalos se hacen más pequeños) estos “escalones” tendrán una dimensión
determinada y se hará un recorrido de todas las combinaciones que puedan darse para
esas dimensiones de datos de entradas, otorgando un valor en función del escalón en el
que caigan. Así, cuando más tarde, se necesite usar la función de activación no se tendrá
que calcular esa función, sino que simplemente se accederá a la LUT con el valor de
entrada y este tendrá asociado su valor de activación. Lo que se gana con esto, es
acelerar el proceso en ejecución, ya que, el proceso costoso (calcular todas las
combinaciones posibles) se hará antes del volcado de código en la FPGA. En la siguiente
figura (figura 3.1.4), se muestran las entradas, salidas, el componente (function sigmoid)
que se encarga de calcular las distintas combinaciones y escribir en memoria (LUT) y el
componente(process) de lectura en memoria (LUT).

44
Figura 3.1.4. Entidad Sigmoid.

Figura 3.1.5. Ejemplo de discretización.

45
4. Por último, se diseña y se implementa la entidad final, la cual consiste en un cómputo
de todas las anteriores o lo que es igual la entidad ANN (figura 3.1.6). Esta entidad se
encargará de crear el número de capas a través de un parámetro genérico y, a su vez,
se encargará de conectar las respectivas salidas de las capas ocultas con las entradas de
las siguientes capas. Cada resultado generado en la salida de una capa se asignará a un
bus de datos que básicamente tendrá un tamaño: 𝐵𝑢𝑠 𝑑𝑒 𝑑𝑎𝑡𝑜𝑠 = (𝑁 𝑑𝑒 𝑛𝑒𝑢𝑟𝑜𝑛𝑎𝑠 ∗
𝑁 𝑏𝑖𝑡𝑠 𝑑𝑒 𝑠𝑎𝑙𝑖𝑑𝑎) − 1 ℎ𝑎𝑠𝑡𝑎 0.

Por ejemplo, si dentro de la capa hay 3 neuronas y las salidas son de 8 bits sería (3*8) -
1 = 23 hasta 0, el dato de la neurona 1 sería desde el bit 0 hasta 7, la neurona 2 desde el
bit 8 hasta el 15, por último, la neurona 3 sería desde el bit 16 hasta el 23.

Una vez se tiene el resultado de la capa en el bus de datos, con el componente


controlador de la entidad ANN se activará una señal que dará paso a la siguiente capa
en la cual sus datos de entrada se alimentarán con los datos de salida de la capa anterior,
formando así la red neuronal hasta que llegue a la última capa donde simplemente se
finalizará la iteración para esos datos de entrada con su correspondiente dato de salida.

La estructura a nivel gráfico se puede observar en la figura 3.1.6, la cual muestra sus
entradas, salidas y la conexión entre las distintas capas.

Figura 3.1.6. Entidad ANN.

46
3.2 Implementación en VHDL
Una vez vista la arquitectura se pasará a la implementación de la misma en VHDL, por lo tanto,
en este apartado se empleará un punto de vista más algorítmico dando así, fuerza a la
arquitectura previamente definida y con ejemplos de código para que sea más sencillo su
análisis.

Se seguirá el diseño de la implementación igual que el de la arquitectura desde los componentes


más pequeños hasta lo más grandes.

3.2.1 Neuron
La entidad neuron a nivel de Input/Output (del inglés, entrada y salida) consta de ocho señales
de entrada, una de salida, dos señales internas y por último tres parámetros genéricos los cuales
son configurables. Las señales de entrada son:

1. CLK, esta señal de entrada es el reloj y es de tipo std_logic (‘0’ o ‘1’), con ella se
mantendrá un proceso síncrono que se activará a nivel alto o lo que es igual cuando la
señal sea = ‘1’.

2. RST, la señal encargada de resetear, al igual que el CLK es de tipo std_logic y su cometido
será una vez que se active a nivel alto se vaciará el contenido de las dos señales internas
de la entidad.

3. A, la señal que contiene un dato de entrada que puede ser el dato de salida de una de
las capas anteriores o el dato de entrada inicial, en cuyo caso, se trataría de la primera
capa de la red neuronal. Esta señal es de tipo std_logic_vector simplemente es un vector
de tipo lógico, por ejemplo, si el vector es de 3 downto 0 sería un vector de bits (‘0000’,
“0001”, …,”1111”) en el cual se podrán hacer 24 = 16 𝑐𝑜𝑚𝑏𝑖𝑛𝑎𝑐𝑖𝑜𝑛𝑒𝑠. En este caso, la
señal de entrada será un vector que irá desde el parámetro genérico nbits-1 downto 0,
lo cual se hace para que el bus de entrada del dato sea configurable y pueda usarse en
distintos proyectos. Como se describió anteriormente la entidad neuron es un
acumulador y una multiplicación, debido a esto la señal será uno de los datos de entrada
que se usarán para multiplicarlo frente al otro vector de entrada el cual será el peso de
esa neurona.
47
4. La señal B será el otro vector de entrada de tipo std_logic_vector y en ella estarán los
pesos de la neurona.

5. BIAS, señal de entrada tiene como finalidad conocer cuál es el valor constante o “bias”
de la neurona. Esta señal de entrada consta con su propio valor genérico, el cual define
la amplitud de su bus de datos.

6. CE1, es una señal de tipo lógica (comúnmente llamada de ‘flag’, del inglés, bandera)
std_logic que se usará para controlar la activación del multiplicador a nivel alto, por lo
tanto, si es ‘0’ no se producirá la multiplicación en ese ciclo de reloj y al contrario si es
‘1’.

7. CE2, otra señal de tipo lógica o flag que se usará para activar el acumulador a nivel alto.

8. Por último, la señal Load es un flag que se emplea para diferenciar entre acumular solo
el peso de la neurona ya que es el peso inicial o acumular el dato anterior más el
resultado de la multiplicación entre el dato de entrada A y B. A nivel alto ‘1’ significará
que se ha de acumular el peso inicial exclusivamente y a nivel bajo se acumulará lo
acumulado anteriormente más el resultado de la multiplicación.

La señal de salida es una exclusivamente y será la señal result. Esta señal es un vector lógico,
pero a diferencia de los vectores lógicos de entrada vistos anteriormente, al tratarse de la salida
en la cual se pueden producir una multiplicación y una acumulación se la han añadido un
numero de bits extra para evitar lo comúnmente conocido como overflow (del inglés,
desbordamiento). También, se añadirán unos bits extras debido a que el bus de datos de “BIAS”
no tiene que ser igual que el de los datos de entrada. Por lo tanto, será un vector que irá desde
(nbits*nFlow*BiasBits) – 1 downto 0, como por ejemplo, si el valor de nbits es 1, nFlow es 2 y
BiasBits es 2, este vector sería (1*2*2 = 4) -1 downto 0, o lo que es igual, 3 downto 0 (‘0000’,
“0001”, …,”1111”). Lo más importante de esta señal de salida es que será asíncrona, ya que,
esta se produce fuera del proceso ‘CLK’.

Por otra parte, las señales internas son todas de tipo In/Out ya que estas son internas del
componente y no son visibles para el resto de los componentes. Estas señales serán de tipo
std_logic y al igual que la variable de salida “result” irá desde (nbits*nFlow*BiasBits) – 1 downto

48
0. Una será la señal Mreg, donde se guardará el resultado de la multiplicación entre “A” y “B” y
la otra será la señal accum, donde se almacenará el resultado de la suma entre el valor de la
iteración anterior y la multiplicación de la iteración actual.

Por último, los parámetros genéricos. Estos parámetros genéricos, son parámetros de tipo
natural configurables para distintos usos del proyecto. “nbits” el cual marca el tamaño del bus
de datos de los valores de entrada y salida. Un ejemplo sería, si deseas tener como entrada un
tipo entero que se representa con 32 bits, este parámetro se configurará con nbits = 32. El
parámetro genérico, que tratará el overflow, “nflow”, y, por último, “BiasBits”, el cual tendrá la
misma utilidad que “nbits” pero para el bus del peso inicial.

Una vez visto las distintas conexiones (puertos, señales y genéricos), se procederá a describir el
proceso funcional.

1. process (CLK)
2. begin
3. if CLK'event and CLK = '1' then
4. if RST = '1' then
5. acc <= (others => '0');
6. Mreg <= (others => '0');
7. else
8. if CE1 = '1' then
9. Mreg <= resize(unsigned(A)*unsigned(B),nbits*nflow*Bias
Bits);
10. end if;
11. if CE2 = '1' then
12. if LOAD = '1' then
13. acc <= resize(unsigned(BIAS),nbits*nflow); --
(bias)
14. else
15. acc <= acc + Mreg;
16. end if;
17. end if;
18. end if;
19. end if;
20. end process;
21. Result <= std_logic_vector(acc);

Como se puede apreciar es un proceso síncrono, cuyo principal evento es el reloj a nivel alto,
por lo tanto, si el reloj está a nivel alto se pasará al siguiente control lógico, que es el reset, si
este está activo se procederá a poner a ‘0’ las señales internas del correspondiente. Sin
embargo, si no está activo las siguientes unidades de control al mismo nivel por lo que se
realizarán en el mismo ciclo, serán los clock enable, tanto como el del multiplicador como el del

49
acumulador. Si el clock enable del multiplicador está a nivel alto se multiplicarán ‘A’ y ‘B’ y el
resultado se asignará a la señal Mreg. Por otro lado, cuando el clock enable del acumulador este
activo si la señal ‘LOAD’ está a su vez activa se acumulará ‘BIAS’ sino se acumulará el resultado
de la iteración anterior con el registro de la multiplicación ‘Mreg’.

Por último, como se ha dicho anteriormente el resultado del acumulador se asignará a la salida
‘result’ de manera asíncrona, esto se hace así ya que como se ha visto en la arquitectura el
control de las iteraciones que realizará este proceso se hará en la entidad que está por encima
del componente neuron. Para que sea más sencillo de entender en el siguiente diagrama de
flujo figura 3.2.1 se mostrará los estados por los que pasará este componente en un ciclo de
reloj a nivel alto de manera gráfica (aunque luego cada instrucción conlleve más ciclos de reloj).

Figura 3.2.1. Diagrama de flujo proceso neuron

50
3.2.2 Sigmoid
Como se expuso en la parte arquitectural de la función de activación de las neuronas, esta
función será la función sigmoid y esta entidad se encargará de realizar dos procesos uno de
escritura en una LUT y otra de controlar el proceso de acceso a la misma.

La estructura de la entidad es:

1. entity Activation_Sigmoid is
2. generic
3. (
4. nbits : natural;
5. nflow : natural;
6. BiasBits: natural;
7. Prange: real;
8. Pinicial: real;
9. step: real
10. );
11. port
12. (
13. reset : in std_logic;
14. clk : in std_logic;
15. run_in : in std_logic; -- Start and input data
validation
16. inputs : in std_logic_vector((nbits*nflow*BiasBits)-
1 downto 0); -- Input data
17. run_out : out std_logic; -- Output data validation, run_in
for the next layer
18. outputs : out std_logic_vector(nbits-1 downto 0) -- Output
data
19. );
20. end Activation_Sigmoid;

Esta tiene seis genéricos los cuales son muy importantes ya que son los datos iniciales de los
que partirá la discretización de la función:

1. nbits: Como en todas las entidades este parámetro es el número de bits del bus de
datos.

2. nflow: Número de bits para evitar el overflow que se trasmite debido al posible
desbordamiento que puede provocar la salida del componente neuron.

3. BiasBits: Número de bits del bus de datos para el puerto de entrada “BIAS” o peso inicial.

4. Prange: Este parámetro será el rango hasta donde irá la función.

5. Pinicial: Parámetro inicial de la función.

51
6. Step: Debido a que se está haciendo una discretización se necesita dar un valor escalón
ya que dependiendo del valor de X se tratará de un escalón u otro.

Por otro lado, se tienen cuatro puertos de entrada y dos de salida:

1. reset: Puerto de entrada para reiniciar los valores de Salida.

2. Clk: Puerto para recibir la señal de reloj.

3. Run_in: Puerto de entrada para saber si hay que hacer el proceso de lectura en la LUT.

4. Inputs: Datos de entrada producidos por una neurona, por lo tanto, este puerto irá
desde longitud (nbits*nflow*BiasBits – 1) downto 0 ya que, en las salidas de las
neuronas, y, el valor de “BIAS” se puede producir un desbordamiento.

5. Run_out: Puerto de salida para dar paso a la siguiente iteración ya que se ha acabado el
proceso de lectura en la LUT.

6. Outputs: Puerto de salida en el que se encuentra el valor con el que se ha activado la


neurona según su salida producida. Este será un vector lógico de nbits hasta 0.

Una vez vistas las señales tanto genéricas como los puertos de entrada/salida en esta
arquitectura hay dos fases muy importantes ya que una será donde se hará el desarrollo de la
función sigmoid y otro el proceso de lectura y su debido control.

En primer lugar, la función sigmoid:

1. signal dataIn: integer range (2**(nbits*nflow*BiasBits)-1) downto 0; -


- To convert std_logic_vector input to integer index for the LUT
2. type table_sigmoid is array(0 to (2**(nbits*nflow*BiasBits))-
1) of std_logic_vector(nbits-1 downto 0); -- RamS
3.
4. -- Function Sigmoidal: generates the Look-Up-Table for the sigmoid
activation function:
5. -- margin: maximun value of x.
6. function Sigmoidal(margin:real;nbits, BiasBits,
nflow:natural) return table_sigmoid is
7. variable scale,real_result, x, result: real;
8. variable F_result: integer;
9. variable fbits: std_logic_vector(nbits-1 downto 0);
10. variable table: table_sigmoid;
11. begin
12. scale := (2.0*margin)/(2.0**(nbits*nflow*BiasBits));
-- sclale in depends of the bits dimensions (for discretitation)
13. x := -margin;

52
14. for idx in -(2**((nbits*nflow*BiasBits)-
1)) to (2**((nbits*nflow*BiasBits) -1))-1 loop
15. result := Prange/(1.0+exp(((-
step*Pinicial)/Prange)*x)) - (Prange/2.0); --density function from
sigmoid
16. real_result := result*(2.0**((nbits*nflow*BiasBits)-
1)); -- without signed
17. real_result := round(real_result); -- rounded the
number for discretation
18. F_result := integer(real_result); -- change to
integer
19. fbits := std_logic_vector(to_signed(F_result,nbits))
; -- convert to vector for saving in the LUT
20. table(to_integer(to_unsigned(idx+(2**(nbits*nflow*Bi
asBits)),(nbits*nflow*BiasBits)))):= fbits; -- save in the LUT
21. x := x+scale; --swap range.
22. end loop;
23. return table;
24. end Sigmoidal;
25.
26. signal Table: table_sigmoid := Sigmoidal(1.0,nbits, BiasBits,
nflow); -- Generation of the LUT (at synthesis time)

La table_sigmoid será un tipo especial (type table_sigmoid) que consistirá en un vector, pero
limitado en función de nbits, nflow y BiasBits, más específicamente será de longitud
2𝑛𝑏𝑖𝑡𝑠 × 𝑛𝑓𝑙𝑜𝑤 × 𝐵𝑖𝑎𝑠𝐵𝑖𝑡𝑠 ya que sé tendrá que tener el mismo número de entradas en la LUT que
posibilidades de datos de entrada. Por otra parte, una vez que se acceda a este vector, el valor
dentro del mismo (este es el valor con el que se activa la neurona) tendrá una longitud igual al
número de bits del parámetro genérico nbits - 1 downto 0.

Para poner un ejemplo más sencillo de entender, si nbits es 2, nflow es 2 y BiasBits es 2 esta
tabla irá desde 0 𝑡𝑜 2(2∗2∗2) − 1 o lo que es igual desde 0 hasta 255 y por lo tanto se tendrá
este número de entradas en la LUT.

Además, estará la función que rellena este tipo “table_sigmoid”. A esta función se le pasará el
margen que se quiere, el número de bits de entrada y el número de bits de overflow. En primer
lugar, se creará una constante que generará los escalones en función de la dimensión de los
datos de entrada y el margen que se asigne, esta constante será en función de la fórmula:
2∗𝑚𝑎𝑟𝑔𝑖𝑛
𝑠𝑐𝑎𝑙𝑒 = . Por ejemplo, el escalón para 4 bits de entrada, 2 de
2𝑛𝑏𝑖𝑡𝑠 ×𝑛𝑓𝑙𝑜𝑤 × 𝐵𝑖𝑎𝑠𝐵𝑖𝑡𝑠
2 ×1
desbordamiento, 1 de bias y un margen de 1 será = 0.0078 lo que significa es que cada
28

escalón de la función será de 0.0078 más que el valor anterior, iniciándose, el primer escalón,
en 0.
53
En segundo lugar, se iniciará la variable X de la función al parámetro de entrada del margen y
se realizará un bucle que recorrerá básicamente lo mismo que el tipo sigmoid solo que este
tratará también los números negativos y por lo tanto el recorrido será:
−(2((𝑛𝑏𝑖𝑡𝑠∗𝑛𝑓𝑙𝑜𝑤∗𝐵𝑖𝑎𝑠𝐵𝑖𝑡𝑠)−1) ) 𝑡𝑜 (2((𝑛𝑏𝑖𝑡𝑠∗𝑛𝑓𝑙𝑜𝑤∗𝐵𝑖𝑎𝑠𝐵𝑖𝑡𝑠) −1) ) − 1. Nótese la resta del 1 para
quitar un bit al vector y así poder tener el mismo número de negativos que de positivos.

Una vez dentro del bucle se define la función de densidad (discretizándola) que será:

𝑃𝑟𝑎𝑛𝑔𝑒
𝑓 (𝑥 ) =
−𝑠𝑡𝑒𝑝∗𝑃𝑖𝑛𝑖𝑐𝑖𝑎𝑙
(( )∗𝑥) 𝑃𝑟𝑎𝑛𝑔𝑒
𝑃𝑟𝑎𝑛𝑔𝑒
(1 + 𝑒 )−(
2 )

Lo siguiente que se hace es quitarle el signo al resultado, redondearlo, convertir el resultado a


vector para poder guardarlo en la LUT y por último asignar este valor a la LUT con el valor en el
que se encuentra del recorrido si por ejemplo se encuentra en el valor 8 entonces a el valor de
la tabla 8 se le asigna el resultado de f(8).

Para finalizar, se cambiará de rango en la escala, por lo tanto, a X (el valor inicial era el margen)
se le sumará el valor scale, anteriormente descrito.

Por otro lado, se definirá el proceso de acceso y control (como se observa en la figura 3.2.2) a
esta LUT la cual se generará en síntesis ya que después de terminar la función se añadirá una
señal:

signal Table: table_sigmoid := Sigmoidal(1.0,nbits, BiasBits, nflow);

Esta llamará a la función descrita anteriormente y al ser una instanciación de una señal se
asegura que se creará en síntesis y no dará problemas de compatibilidad.

Por lo tanto, se tiene la LUT en la señal Table, ahora bien, el proceso funcional será:

1. begin
2.
3. -- Description of the activation function
4. dataIn <= to_integer(unsigned(inputs));
5. Activation: process(clk,reset)
6. begin
7. if clk'event and clk = '1' then
8. if reset = '1' then
9. run_out <= '0';
10. outputs <= (others => '0');
54
11. else
12. if run_in = '1' then
13. run_out <='1';
14. outputs <=Table(dataIn); -- Assigns output
value from the LUT
15. else
16. run_out <='0';
17. end if;
18. end if;
19. end if;
20. end process;

En primer lugar, se transforma el vector de entrada a entero para poder acceder a su correcta
posición en la LUT, luego sigue el proceso que, al ser síncrono, se activa cuando la entrada de
reloj esté a nivel alto. Si el puerto de entrada reset está activo se reinician las salidas, si no se
continua con el proceso de acceso a la LUT en la que se encuentran los resultados de la función
sigmoid. Por consiguiente, lo primero y único que se comprobará será si el puerto de entrada
run_in está activa ya que significará que el resultado en la neurona asociada estará listo, se
activará la señal que da paso a la siguiente parte del proceso de la entidad neuron_top y se
asigna valor al dato de salida “outputs” según el valor de la LUT en función del valor “dataIn”.
Todo este proceso se puede observar en la siguiente figura 3.2.2, la cual, es un diagrama de
flujo que describe el proceso anterior.

Figura 3.2.2. Diagrama de flujo proceso sigmoid.


55
3.2.3 Neuron Top
Esta entidad será la encargada de controlar las entidades descritas anteriormente.

1. entity neuron_top is
2. Generic(
3. NnAnt: natural;
4. BiasBits: natural;
5. nbits: natural --- Bit width of the input data
6. );
7. Port (
8. DATAIN: in STD_LOGIC_VECTOR((nbits*NnAnt)-1 downto 0);
9. CLOCK: in STD_LOGIC;
10. Wdata: in STD_LOGIC_VECTOR((nbits*NnAnt)-1 downto 0); -
- Data weight port
11. Start_neuron: in STD_LOGIC;
12. Init_neuron: in STD_LOGIC_VECTOR((nbits*BiasBits)-
1 downto 0); -- bias neurons
13. RESET: in STD_LOGIC; -- reset
14. Fneuron: out std_logic; -- finish that iteration
15. Odata: out STD_LOGIC_VECTOR(nbits-1 downto 0)
16. );
17. end neuron_top;

Los puertos de entrada/salida y los parámetros genéricos de este componente son los
siguientes:

1. NnAnt número de neuronas de la capa anterior.

2. BiasBits número de bits del bus de datos del peso de la neurona inicial o “bias”.

3. nbits número de bits del bus de datos.

A continuación, se definen los puertos de entrada que son:

1. DATAIN o lo que es igual datos de entrada, lo más importante de este puerto es que
como se puede apreciar en el trozo de código anterior va desde longitud (nbits*Nnant)
– 1 downto 0, esto significa que será un puerto de entrada que depende de la cantidad
de neuronas que había en la capa anterior donde, por ejemplo, si se tienen 3 neuronas
en la capa anterior y el bus de datos son de dos bits este vector tendrá como límite de
su índice desde 5 hasta 0.

2. CLOCK, el reloj.

3. Wdata, este es el vector de los pesos al igual que los datos de entrada este depende de
las neuronas de la capa anterior.
56
4. Start_neuron, es el puerto de entrada que da lugar al inicio del controlador.

5. Init_neuron, la entrada del peso inicial o bias.

6. RESET, señal de reinicio para poner a ‘0’ todos los registros.

Los de salida son dos, uno para indicar que se ha finalizado la iteración y se tiene el resultado
disponible (Fneuron) y 0data que es el vector de salida o el resultado que estará listo cuando la
otra señal se active.

Por otro lado, las señales internas de este componente son:

1. signal weight:Weights(0 to NnAnt):= (others => (others => '0')); --


weights of before layer + BIAS
2. signal CEM:STD_LOGIC:= '0'; -- Multiplicador ENable
3. signal CEA:STD_LOGIC:= '0'; --Accum Enable
4. signal LOADa:STD_LOGIC:= '0'; -- Load signal for bias
5. signal NeuronFinish:STD_LOGIC:= '0'; --FLAG FOR Neuron finish
6. signal SigmoidFinish:STD_LOGIC:= '0'; --FLAG FOR Neuron finish
7. signal ResetN: STD_LOGIC:= '0'; -- RESET for the neurons
8. signal ResetS: STD_LOGIC:= '0'; -- RESET for the sigmoid
9. signal A weights: natural:= 0; -- counter for swapping between the
distinct neurons
10. signal Ainput: natural := 0; -- counter for swapping between the
distinct input data bus
11. signal DataInVector:Weights(0 to NnAnt-
1):= (others => (others => '0')); -- Data input vector for select
inputs
12. signal weight_Logic: STD_LOGIC_VECTOR(nbits-
1 downto 0):= (others => '0'); --signal for weight to continue to a
neuron process
13. signal DataIn_logic: STD_LOGIC_VECTOR(nbits-
1 downto 0):= (others => '0'); --signal for datain to continue to a
neuron process
14. signal OdataN: STD_LOGIC_VECTOR((nbits*nflow*BiasBits)-
1 downto 0):= (others => '0'); --signal for the data output produces
by the neuron component
15. type estado is (e0,e1,e2,e3,e4);
16. signal presente:estado;

Todas ellas son por lo general señales auxiliares para facilitar el proceso que va a controlar los
componentes “neuron” y “sigmoid”. Por otro lado, la señal de estado, que se usará para realizar
un diagrama de flujo que constará de cinco estados.

En primer lugar, en esta arquitectura existen tres partes diferenciales, las cuales se tratarán de
manera independiente, aunque sean totalmente complementarias, incluso, dependientes para
que sea más sencillo de explicar. La primera parte de un proceso con lista de sensibilidad los

57
puertos CLOCK, Init_neuron, Wdata, DATAIN, y RESET. Dentro de este proceso se distinguen
dos apartados un bucle for y un evento síncrono o lo que es igual un evento de reloj:

• En el bucle for lo que se hará es rellenar los distintos datos alojados en los puertos
de entrada Wdata y DATAIN a las señales auxiliares “Weight” y DataInVector,
ambas vectores enteros, con el fin de que sea más fácil acceder a ella según el
estado en el que se esté del flujo.

1. process(CLOCK, Init_neuron, Wdata, DATAIN, RESET)


2. begin
3. for i in 0 to NnAnt loop
4. Weight (i)<= Wdata((nbits*(i+1)-1) downto i*2);
5. DataInVector(i) <= DATAIN((nbits*(i+1)-1) downto i*2);
6. end loop;

• Por otro lado, como se ha dicho, dentro del proceso se realizará un flujo de datos
que irá desde el estado “e0” al “e4” o lo que es igual cinco estados donde se
controlarán cuantas iteraciones se tienen que hacer, si la neurona tiene que
multiplicar, acumular o si se tiene el resultado para pasarlo al componente sigmoid,
etc. Como se puede ver en el siguiente diagrama de flujo figura 3.2.3 según en el
estado en el que se encuentre el proceso se harán unas determinadas acciones.

1. El estado cero es el estado inicial en el cual se comprobarán si la señal reset está activa,
el puerto “Start_neuron” está desactivado o se ha finalizado la iteración (NeuronFinish)
como consecuencia se reiniciarán todas las salidas y señales, además, se entrará en el
bucle sobre el estado inicial. Sin embargo, si ocurre lo contrario se comprobará si es la
primera iteración, si es, se acumula el peso inicial, “BIAS”, ya que no hace falta
multiplicar y se procederá a ir al estado “e2”. Por otro lado, si no es la primera iteración
se multiplicará el dato de entrada por el peso correspondiente a esa neurona asociado
a ese dato de entrada y se irá al estado “e1”.

2. En el estado uno ya se tendrá el resultado de la multiplicación del peso por la entrada


correspondiente por lo cual se procederá a activar el flag que dará paso a la suma de la
multiplicación con el propio registro de acumulación (debido a las iteraciones
anteriores) y se pasará al estado tres o “e3”.

58
3. En el estado dos solo se llegará si es la primera iteración de todas ya que consiste en
acumular el peso inicial o “bias”, por lo que solo se dejará un ciclo de reloj para que
acumule y en este estado se aumentará el contador “Aweights” (contador que se usa
para delimitar el número de iteraciones en función de las neuronas de la capa anterior)
y se vuelve al estado inicial 0 o “e0”.

4. En el estado tres ya se tendrá la acumulación en el registro por lo que se desactiva el


flag que permite a la neurona acumular y se pasará al estado 4 o “e4”.

5. Por último, en el estado cuatro se comprobará si el número de iteraciones que se lleva


es menor que el número de neuronas de la capa anterior y por lo tanto pueden ocurrir
dos cosas. Que la condición sea verdad por lo tanto es menor y se volverá al estado
inicial “e0”, sumando uno al número de iteraciones y cambiando el dato de entrada. Por
el contrario, que el número de iteración sea igual al número de neuronas de la capa
anterior provocando la activación del flag “NeuronFinish” para indicar que la neurona
Nx ha terminado el proceso y volviendo al estado “e0” hasta que se vuelva a iniciar el
proceso.

Todo lo anterior, con las diferentes transiciones entre los distintos estados, en forma de
diagrama de flujo, se muestra en la siguiente figura.

59
Figura 3.2.3. Diagrama de flujo proceso Neuron top.

3.2.4 Layer Generation


Esta entidad será la encargada de hacer un sencillo proceso de control y de crear una capa o
layer de la red neuronal, entendiendo como layer el conjunto formado por un número finito de
neuronas con sus pesos asociados a las entradas (en el caso de ser el layer inicial) o a las
neuronas del layer anterior.

A continuación, se muestra la entidad según está definida.

1. entity Layer_Generation is
2. Generic(
3. nbits: natural;
4. NN: natural;
5. NnAnt: natural;
6. id: natural:=0
7. );
8. Port (
60
9. DATAIN: in STD_LOGIC_VECTOR((nbits*NnAnt)-1 downto 0);
10. CLOCK: in STD_LOGIC;
11. Start_layer: in STD_LOGIC;
12. RST: in STD_LOGIC;
13. Fneurons: out STD_LOGIC;
14. Odata: out STD_LOGIC_VECTOR((nbits*NN)-1 downto 0) --DATAOUT
is a vector to group all Outs of de neurons
15. );
16. end Layer_Generation;

Este componente consta de cuatro puertos de entrada, dos de salida, cuatro variables
genéricas y el componente neuron_top.

Señales Genéricas:

1. nbits: Número de bits del bus de datos.

2. NN: Número de neuronas del layer a tratar.

3. NnAnt: Número de neuronas del layer anterior o del número de entradas si es la capa
inicial.

4. Id: Este elemento es un id asociado a cada layer para distinguir entre capas iniciales,
ocultos y finales.

Puertos de entrada y de salidas

1. RST: Puerto de entrada para reiniciar los valores de salida.

2. CLOCK: Puerto de entrada para la señal de sincronización de reloj.

3. DATAIN: Puerto de entrada de datos.

4. Start_layer: Puerto de entrada que da el paso inicial para iniciar las operaciones del layer
en cuestión.

5. Fneuron: Puerto de salida el cual da por finalizada la iteración de esta capa.

6. Odata: Puerto de salida por donde irán los datos de salida de este componente

Para explicar su funcionamiento, implementación, se menciona en primer lugar que, como en


este proyecto se está tratando la propagación hacia delante (del inglés, forward propagation)
los pesos de las neuronas estarán fijos en este componente por lo que, se hará un pequeño
proceso previo para rellenar el vector de pesos del layer que está siendo creado como

61
componente genérico. En este proceso se dividirán en dos vectores, uno para el peso inicial de
las neuronas (bias) y otro con el resto de los pesos de la matriz. Como puede observarse en el
código mostrado a continuación:

1. for i in 0 to NN-1 loop


2. Init_neuron(i) <= bias_value(i); --bias
3. for t in 0 to NnAnt-1 loop
4. weight_Logic(i)((nbits*(t+1)-1) downto t*2) <= W(i,(t));
5. end loop;
6. end loop;

En este código se puede observar la manera en la que se crean los dos vectores descritos
anteriormente, para más tarde pasar a cada neurona sus pesos frente al layer anterior
correctamente. Como no se pueden pasar vectores multidimensionales como si fueran señales
del siguiente componente (en este caso componente neuron) el modo de desarrollo que ha
sido tratado es el siguiente:

1. Se tiene una matriz W con los pesos en memoria, por lo que se recorrerá con un doble
for el primero para iterar sobre filas y el siguiente sobre columnas.

2. También, en otro vector, se tienen los valores asociados al peso inicial o bias.

3. A continuación, se inserta los valores en el array de manera secuencial y ordenadamente


para que en los componentes anteriores no haya errores a la hora de acceder al vector
de bits, por ejemplo, en el caso de que el layer anterior tenga 2 neuronas con 2 bits de
longitud cada peso, se tiene un vector con X neuronas y cada neurona tendrá asociado
neurona(x) = “0100” y el valor de bias(x) = “11” por lo que cuando se asigne al
componente neurona los pesos este sabrá que el valor del peso para la primera neurona
de la capa anterior será “00”, para la segunda será “01” y para la de inicial o bias “11”.
Como se observa de manera más simple en la figura 3.2.4.

Figura 3.2.4. Ejemplo de carga de pesos.

62
En segundo lugar, se hará una comprobación dentro del proceso para determinar si han
finalizado todas las neuronas de hacer sus operaciones. Esto se hace simplemente para que
el componente superior a este sepa que ya están terminadas todas las operaciones del
layer. También tiene un propósito adicional, que no haya condiciones de carrera como se
puede observar en el siguiente código:

1. constant all_ones : std_logic_vector (NN-


1 downto 0) := (others => '1');
2. if Fneuron_signal = all_ones then
3. Fneurons <= '1';
4. else
5. Fneurons <= '0';

Por último, la generación del componente descrito anteriormente Neuron_Top, en el cual


se crean tantas neuronas como tenga la capa tratada. Como se observa en el siguiente
código:

1. neuron:for r in 0 to NN-1 generate


2. neuron: neuron_top generic map(NnAnt,BiasBits,
nbits) port map(DATAIN => DATAIN,CLOCK => CLOCK,
Wdata =>weigth_Logic(r),Start_neuron => Start_layer,Init_neuron => Ini
t_neuron(r),
RESET => RST,Fneuron => Fneuron_signal(r),Odata => Odata((nbits*(r+1))
-1 downto (nbits*r)));
3. end generate neuron;

3.2.5 Top Layers


Para finalizar la parte de desarrollo, está es la entidad superior y en ella se declarará cuantas
capas tendrá la red neuronal, cuantas neuronas cada layer, el número de bits de datos, etc.
Básicamente, es la parte declarativa de la red neuronal.

1. entity top_layers is
2. Generic(
3. nbits: natural;
4. NIn: natural;
5. NtotalLayers: natural;
6. NLayerInit: natural;
7. NLayerOthers: natural;
8. NLayerFinish: natural
9. );
10. Port (
11. DATAIN: in STD_LOGIC_VECTOR((nbits*NIn)-1 downto 0);
12. CLK: in STD_LOGIC;
13. RESET: in STD_LOGIC;
14. start_ANN: in STD_LOGIC;
63
15. OdataANN: out STD_LOGIC_VECTOR(nbits-1 downto 0);
16. FinishANN: out STD_LOGIC
17. );
18. end top_layers;

Esta entidad cuenta con 4 puertos de entrada, 2 de salida, 6 genéricos y como en todas las
entidades el componente anterior layer generation.

Señales Genéricas:

1. nbits: Número de bits del bus de datos.

2. NIn: Número de datos de entrada.

3. NtotalLayers: Número total de layers.

4. NLayerInit: Número de neuronas de la capa inicial ya que se necesita distinguir entre los
iniciales, los ocultos y el de salida.

5. NLayerOthers: Número de neuronas de las capas ocultas, todas ellas tendrán el mismo
número de neuronas.

6. NLayerFinish: Número de neuronas del layer final, si son varias se estaría realizando una
regresión y si es una neurona sería una clasificación.

Puertos de entrada y de salidas:

7. RESET: Puerto de entrada para reiniciar los valores de todos los componentes.

8. CLOCK: Puerto de entrada para controlar (la sincronización) los ciclos de reloj de todos
los componentes.

9. DATAIN: Puerto de entrada de datos, el tamaño variará dependiendo del tamaño del
bus de datos y del número de datos de entrada.

10. Start_ANN: Puerto de entrada que da el paso inicial para iniciar la red neuronal.

11. FinishANN: Puerto de salida que dará por finalizada una iteración completa de la red
neuronal.

12. OdataNN: Puerto de salida de la red neuronal.

64
Una vez visto, los puertos de la entidad lo único que se hará en este componente es
parametrizar todos los componentes según los datos genéricos que se han definido.

Para ello se han realizado cinco tipos distintos de generaciones del componente
layer_generation, ya que, las señales y los puertos tienen que ir conectados de manera distinta.

En primer lugar, se declara un for que generará el número de capas que se hayan definido en
el puerto genérico NtotalLayers. La primera instanciación es el layer inicial porque en este
caso a los valores genéricos del componente layer_generation se le asignará el número de
neuronas iniciales que se elijan para esta capa, luego el número de neuronas anterior sería el
mismo número de entradas y por último el id el 0.

1. ANN:for r in 0 to NtotalLayers-1 generate


2. layer_init : if (r = 0) generate --Layer In
3. LI: Layer_Generation generic map (nbits, NLayerInit, NIn,
id => r)

Por otro lado, los puertos asignados serán:

1. port map (DATAIN => DATAIN, CLOCK => CLK, Start_layer => start_ANN, RST
=> reset_all, Fneurons => Flayer(r), Odata => OlayerInit);

Los datos de entrada en este caso son los que vengan del mismo puerto, luego el inicio del layer
será el inicio de la red neuronal y el puerto de salida será una señal para pasar como puerto de
entrada DATAIN del siguiente.

En la segunda instanciación se trata el caso en el que solo existan dos layer en la red neuronal
ya que podría no haber capas ocultas y existir, solamente, un layer inicial y otro final. Para ello
se comprueba primero que haya como máximo dos capas y además se esté en el layer final.

1. layer_finish2: if (r = (NtotalLayers-1) and NtotalLayers = 2) generate


2. LF2: Layer_Generation generic map (nbits, NLayerFinish,NLayerInit,
id => r)

El número de neuronas será el definido con el genérico NLayerFinish, el número de neuronas


anterior será el número de neuronas del layer inicial.

Los puertos asignados esta vez serán:

65
1. port map (DATAIN => OlayerInit, CLOCK => CLK, Start_layer => Flayer(r-1),
RST => reset_all, Fneurons => Flayer(r), Odata => OdataANN);

Los datos de entrada son los datos de salida del layer inicial, el inicio de esta capa será el final
de la capa inicial, el final del layer será el final de la iteración de la red neuronal y, por último,
los datos de salida serán los datos de salida de la red neuronal.

A continuación, hay dos instanciaciones que son parecidas y son las de las capas ocultas. Hay
una diferencia entre una y otra y es simplemente que la que es posterior al layer inicial hay que
instanciarla de manera distinta por lo que a esta se nombrará “LX1” y a la genérica “LX”. Primero
se desarrollará la genérica ya que “LX1” es un cambio simple. Como se muestra en el siguiente
código:

1. layerX: if (r > 1 and r < (NtotalLayers-1)) generate


2. LX: Layer_Generation generic map (nbits, NLayerOthers,
NLayerOthers, id => r)

Si se está en el caso de que no sea el layer inicial ni el final, es un layer oculto por lo que tanto
el número de neuronas del layer como las del layer anterior serán iguales.

Los puertos asignados de los layers ocultos “LX” serán, como se muestran a continuación:

1. port map (DATAIN => OlayerOthers(r-1), CLOCK => CLK, Start_layer =>
Flayer(r-1), RST => reset_all, Fneurons => Flayer(r), Odata =>
OlayerOthers(r));

Los datos de entrada serán los datos de salida del layer oculto anterior y los datos de salida
serán asignados a una señal, la cual es un vector con los datos de salida de cada capa oculta
para poder asignarle a los datos de entrada de la siguiente capa oculta.

La diferencia entre “LX1” y “LX” está en que el número de neuronas de la capa anterior es el
número de neuronas del layer inicial y que la entrada de datos es la salida del layer inicial.

Por último, sería la instanciación de la capa final genérica (en el cual existen capas ocultas). Lo
anterior, codificado, se muestra a continuación:

1. layer_finish: if (r = (NtotalLayers-1)) generate


2. LF: Layer_Generation generic map (nbits, NLayerFinish, NLayerOthers,
id => r)

66
Si se está en el caso final, el número de neuronas dependerá si será una regresión o una
clasificación, luego el número de neuronas del layer anterior será el que tengan las capas
ocultas.

Para dar por concluido este capítulo, la asignación de puertos, en este caso, será como se
muestra en el siguiente código:

1. port map (DATAIN => OlayerOthers(r-1), CLOCK => CLK, Start_layer =>
Flayer(r-1), RST => reset_all, Fneurons => Flayer(r), Odata => OdataANN);

Los datos de entrada DATAIN serán el último valor del vector asociado a los layers ocultos, el
final de este layer corresponderá con el final de una iteración de la red neuronal y los datos de
salida serán los datos de salida de la red neuronal.

67
68
Capítulo 4: Resultados
En este capítulo se abordará todo lo que respecta a resultados de simulación de las distintas
entidades y un ejemplo funcional del proyecto en conjunto. Para ello, como se ha explicado
anteriormente, se trata de una arquitectura bottom-to-top, por lo que, del mismo modo, se
definirán las distintas simulaciones funcionales. En estas simulaciones o pruebas se
comprobarán que todas entidades cumplen los requisitos definidos en el desarrollo,
asegurando así, ningún comportamiento erróneo.

4.1 Simulación de la entidad neuron


En primer lugar, la entidad más básica. Para comprobar su correcta funcionalidad, se analizarán
las distintas condiciones del proceso, provocando estímulos diferentes en las señales de
entrada de la entidad. Cabe destacar, que, a pesar de que los estímulos están en binario los
resultados se mostrarán en hexadecimal o decimal por el hecho de que es más fácil y visual.

Figura 4.1.1. Simulación entidad neuron, primera comprobación.

El primer requisito a comprobar es que, se produzca la multiplicación cuando el primer


clock enable está activo. Como se puede observar en la figura 4.1.1, con estímulos de
entrada A = “2”, B = “2”, CE1 = ‘1’, en el registro Mreg tendrá que estar su multiplicación o

69
lo que es igual, Mreg = “4”. A su vez, se puede comprobar que el resultado P y el acumulador
están con valor a “0”, lo cual, es correcto.

La siguiente comprobación, será desactivar CE1, activar el CE2, dando como resultado que
el acumulador tendrá el valor multiplicado anteriormente o lo que es igual acc = “4” y la
salida P igual que el acumulador. En la figura 4.1.2 se comprueba este caso.

Figura 4.1.2. Simulación entidad neuron, segunda comprobación.

A continuación, se comprobará que si el CE2 = ‘1’ y a su vez el LOAD = ‘1’ el acumulador será
igual al valor de entrada de BIAS, que, en este caso será 1. Como se muestra en la figura 4.1.3

Figura 4.1.3. Simulación entidad neuron, tercera comprobación.


70
Por último, comprobar que, el estímulo de reset reinicia los valores de Mreg y acc como se
muestra en la figura 4.1.4, que, estando activo la señal reset, los valores de las señales han
vuelto a su valor inicial “0”.

Figura 4.1.4. Simulación entidad neuron, cuarta comprobación.

4.2 Simulación de la entidad sigmoid


Para poder comprobar el correcto funcionamiento de esta entidad, se hará uso del proceso de
creación de la función sigmoid descrita en el capítulo 3, apartado 3.2.3.

Por lo tanto, si se tiene un bus de datos de 2 bits, 2 bits para evitar posibles desbordamientos y
1 bit en el bus de datos de “BIAS”, la LUT tendrá una capacidad de almacenamiento de
2(2∗2∗1) = 16 posibles combinaciones. Para poder facilitar el entendimiento de los ejemplos,
según los estímulos de entrada, se definirá una tabla de verdad que simula el resultado de la
función sigmoid con valores genéricos: BiasBits igual a 1 y nbits, nflow, Prange, Pinicial, step
igual a 2.

71
a b c d out

0 0 0 0 0

0 0 0 1 1

0 0 1 0 2

0 0 1 1 3

0 1 0 0 0

0 1 0 1 0

0 1 1 0 1

0 1 1 1 2

1 0 0 0 2

1 0 0 1 2

1 0 1 0 3

1 0 1 1 0

1 1 0 0 0

1 1 0 1 1

1 1 1 0 2

1 1 1 1 3

Tabla 4.2.1. Tabla de verdad función sigmoid.

En la siguiente figura, se puede observar como con la señal de entrada, run_in, activa (indicando
que se puede acceder a la tabla que guarda los resultados de la función sigmoid) y un valor de
entrada de “1000” se obtiene en la salida un “2”, ya que, si se observa en la tabla 4.2.1 el valor
asociado a el “1000” es “2”. En este apartado se puede observar que cuando se produce una
salida, también, se genera una señal de salida “run_out”, encargada de avisar a la entidad
superior.

72
Figura 4.2.1. Simulación entidad sigmoid, primera comprobación.

El siguiente y último requerimiento en este módulo, es que, si el valor de reset se activa, tanto
el valor de “outputs” y “run_out” deberán volver a su estado original, como se puede apreciar
en la figura 4.2.2 mostrada a continuación.

Figura 4.2.2. Simulación entidad sigmoid, segunda comprobación.

4.3 Simulación de la entidad neuron top


En primer lugar, esta entidad hace uso de las dos anteriores, por lo que, a la hora de ejemplificar
se recapitularán algunas operaciones descritas previamente.

En la simulación funcional, los valores genéricos usados, son:

1. Bus de datos, “nbits” igual a 2.

2. Bus de datos, “BiasBits” igual a 1.

3. Número de neuronas anteriores, “Nant” igual a 1.

4. El resto de los parámetros genéricos pertenecientes a las entidades anteriores, seguirán


siendo los mismos usados en las simulaciones funcionales anteriores.
73
La primera condición, es que, para entrar en el primer estado, tiene que estar activa la señal
que da paso a iniciar el proceso “Start_Neuron” y tanto el “RESET” como la señal de salida
“NeuronFinish” estén desactivadas. Una vez, cumplida dichas condiciones, se procederá a
acumular el valor de entrada procedente de “bias”, debido a que se encuentra en la primera
iteración del proceso. En este caso, ese valor inicial “bias”, procede de “Init_neuron” con un
valor de “11” o 3 en decimal, dando como consecuencia una salida de la entidad neuron de 3,
como se observa en la figura 4.3.1.

Figura 4.3.1. Simulación entidad neuron_top, primera comprobación.

A continuación, se procederá a multiplicar el valor de entrada “DATAIN” con valor “10”, por
el peso de la neurona que se ha establecido en “11”, por lo que el resultado será “0110”, o,
en decimal, 3 x 2 = 6. Como el acumulador de la entidad neuron, tenía un 3 procedente del
paso anterior, el resultado final, será la suma de ambos, 6 + 3 = 9, en binario, “1001”.
Simulación mostrada en la siguiente figura:

74
Figura 4.3.2. Simulación entidad neuron_top, segunda comprobación.

Para finalizar, la salida anterior de la entidad neuron se pasará a la entrada de la entidad


sigmoid, y, para saber el resultado se analizará la tabla 4.2.1, el valor “1001” da como lugar una
salida “2”, por lo tanto se activará la señal de “Fneuron”, con un valor de salida de la entidad
neuron_top igual al valor de la salida de la entidad sigmoid, como se puede observar en la
siguiente figura 4.3.3.

Figura 4.3.3. Simulación entidad neuron_top, tercera comprobación.

También, hay que recalcar, que, en esta simulación al tener la variable genérica “Nant”, con
solo una neurona anterior, solo existirá una iteración del proceso. Pero este proceso se repetiría
tantas veces como neuronas anteriores se tengan.

75
4.4 Simulación de la entidad Layer generation
Para la simulación de esta entidad algunos parámetros genéricos cambiaran, ya que, se vuelve
más tedioso el seguimiento del proceso.

Los valores genéricos usados serán:

1. Bus de datos, “nbits” igual a 1.

2. Número de neuronas anteriores, “Nant” igual a 2 (en este caso las neuronas anteriores
serán los valores de entrada).

3. Número de neuronas del layer, “NN” igual a 2, por lo que, se tendrá 2 entidades
“neuron_top”.

4. Identificador del layer, “id” igual a 0.

5. El resto de los parámetros genéricos pertenecientes a las entidades anteriores, seguirán


siendo los mismos usados en las simulaciones funcionales anteriores.

1 1 1
Con los valores genéricos anteriores, y, la matriz de pesos , se genera una estructura
0 1 1
como la que se puede observar en la siguiente figura 4.4.1.

Además, se genera una LUT con los valores generados por la función sigmoid tal que:

X1 X2 Out
0 0 0
0 1 1
1 0 0
1 1 1
Tabla 4.4.1. Tabla de verdad función sigmoid, bus de datos de 1 bit.

76
Figura 4.4.1. Estructura generada para la simulación de la entidad layer generation.

Como se puede observar en la figura 4.4.1, si se realizase el proceso definido en el desarrollo


para este ejemplo, se tendría:

Neurona “N1”: En primer lugar, se le sumaría el peso de la neurona “Bias” por lo que, en
“N1” se tendría un resultado de “1”. En la segunda iteración, se multiplicaría el valor de
entrada “X1” por el peso de “X1-N1”, o lo que es igual, 1*1 = 1 y se le sumaría el resultado
anterior, dando lugar, “N1” = 2. En la última iteración, se multiplicaría, el valor de entrada
“X2” por el peso de “X2-N1”, 0*1 = 0, obteniendo un resultado final en “N1” de 2. Para
finalizar, el resultado de “N1” (en este caso en binario sería “10”) se accedería a la LUT
donde están guardados los resultados de la función sigmoid, y, se obtiene que para el valor
“10” el resultado es un “0” (como se aprecia en la tabla 4.4.1).

Neurona “N2”: El mismo proceso que la neurona anterior, primero se le sumaría el peso de
la neurona “Bias” por lo que, en “N2” se tendría un resultado de “0”. En la segunda iteración,
se multiplicaría el valor de entrada “X1” por el peso de “X1-N2”, o lo que es igual, 1*1 = 1 y
se le sumaría el resultado anterior, dando lugar, “N2” = 1. En la última iteración, se

77
multiplicaría, el valor de entrada “X2” por el peso de “X2-N2”, 0*1 = 0, obteniendo un
resultado final en “N2” de 1. Para finalizar, el resultado de “N2” (en este caso en binario
sería “01”) se accedería a la LUT donde están guardados los resultados de la función sigmoid,
y, se obtiene que para el valor “01” el resultado es un “1” (como se aprecia en la tabla 4.4.1).

Este mismo proceso, pero con la simulación funcional producida por la entidad, se puede
apreciar en la siguiente figura:

Figura 4.4.2. Simulación funcional de la entidad layer generation.

El proceso es el mismo descrito anteriormente, pero hay que clarificar dos circunstancias que
pueden llevar a error.

El primer factor a tener en cuenta, son los vectores de entrada y de salida. Aunque, en el
apartado de desarrollo ya se ha explicado, debido a que en las entidades no se pueden definir
puertos de tipo matricial lo que se hace es dividir un vector de entrada lógico en n partes
dependiendo del número de neuronas anteriores. Por lo tanto, en este ejemplo el vector de
entrada tiene que ser de tamaño 2 bits, uno para la entrada “X1” y el otro para la entrada “X2”,
más específicamente, si el vector de entrada es 01, la entrada “X1” tendrá un valor de “1” y la
entrada “X2” un valor de “0”. Lo mismo con el valor de salida, “Odata” es igual a “10” por lo
que, la neurona “N1” tiene un valor de “0” y la neurona “N2” de “1”.

El segundo factor a tener en cuenta es que el resultado no está disponible hasta que todas las
neuronas han terminado su proceso. Esta casuística se puede apreciar cuando la señal
“Fneurons“se activa a nivel alto. La señal también produce que se reinicien los valores de salida

78
de las neuronas, por lo tanto, para observar los resultados de las neuronas se tienen que
observar un ciclo de reloj antes.

4.5 Simulación de la entidad top Layers


Para finalizar con las simulaciones funcionales, la entidad en la que se engloban todas las
demás, y, que es la encargada de generar la red neuronal como se ha explicado anteriormente.
Para entender mejor la visualización y el entendimiento de que funciona correctamente, se va
a realizar un clasificador que simulará la puerta lógica XNOR.

Para ello, es importante saber cómo se forma una XNOR y su tabla de verdad. La puerta lógica
XNOR, con representación lógica, 𝐴 . 𝐵 + 𝐴̅ . 𝐵̅ , se puede realizar con otras puertas lógicas, para
ser exactos, una de las posibilidades es mediante el uso de una puerta AND, una NOR y una OR.
A continuación, en la tabla 4.5.1 se observa su tabla de verdad.

X1 X2 N1(X1 AND X2) N2(X1 NOR X2) N3(N1 OR N2)


0 0 0 1 1
0 1 0 0 0
1 0 0 0 0
1 1 1 0 1
Tabla 4.5.1. Tabla de verdad XNOR.

Por consiguiente, para realizar esta misma expresión lógica usando una red neuronal que
clasifique si el valor de entrada produzca las mismas salidas que se generarían en una puerta
lógica XNOR, se hará uso de varias figuras.

En la primera figura 4.5.1 mostrada a continuación, se muestra el valor de los pesos que
deben tener las neuronas y bias, para la obtención de la puerta lógica x1 AND x2, teniendo en
cuenta varios factores:

1. El bus de datos de bias, es de 2 bits y resto de pesos de 1 bit.

2. Los resultados de la función sigmoid almacenada en el ejemplo, se puede observar en


la tabla 4.5.2.

3. Los pesos, son puestos a propósito ya que no existe backpropagation no se recalculan.

79
Figura 4.5.1. Red neuronal que clasifica una puerta lógica AND.

Por otro lado, la puerta lógica NOR, teniendo en cuenta los puntos anteriores sería como en
la figura 4.5.2.

Figura 4.5.2. Red neuronal que clasifica una puerta lógica NAND.

A continuación, la puerta lógica OR, figura 4.5.3, misma situación que en las anteriores.

80
Figura 4.5.3. Red neuronal que clasifica una puerta lógica OR.

Estas son las puertas lógicas que se necesitan para forma la XNOR. Ahora si se agrupan todas
quedaría una red neuronal con 2 entradas y dos capas. En la capa 2, se tendrían dos
neuronas y en la capa 3 o capa de salida se tendría una neurona. El resultado se puede
observar en la siguiente figura. Una vez vista la estructura generada, la tabla con los
resultados de la función sigmoid asociada a este ejemplo, consta de 16 entradas, debido a
que el valor BiasBits es igual a 2 y nflow igual a 2 se genera una tabla de 24 = 16.

81
a b c d out

0 0 0 0 0

0 0 0 1 0

0 0 1 0 1

0 0 1 1 1

0 1 0 0 0

0 1 0 1 0

0 1 1 0 1

0 1 1 1 1

1 0 0 0 1

1 0 0 1 1

1 0 1 0 1

1 0 1 1 0

1 1 0 0 0

1 1 0 1 1

1 1 1 0 1

1 1 1 1 0

Tabla 4.5.2. Resultados función sigmoid, 2 de Bias, 1 de datos y 2 de overflow.

A continuación, se en la figura 4.5.4 se muestra la agrupación obtenida de las distintas redes


neuronales asociadas a las puertas lógicas AND, NOR y OR.

82
Figura 4.5.4. Red neuronal que clasifica una puerta lógica XNOR.

Por consiguiente, realizando el proceso paso a paso, por ejemplo, para unos valores de entrada
X1 igual a 0 y X2 igual a 0:

1. En la primera iteración se tendría que N1(operación AND) con valores de entrada 00,
cuando se acumula la neurona inicial un 0, ya que, el peso asociado es 00. Sin embargo,
en N2(operación NOR), se acumularía el peso asociado 11, por lo que se obtiene un
resultado de 3.

2. Para la segunda iteración, se tendría que X1, siendo igual a 0 multiplicado por el peso 1,
daría un 0 como resultado. Por otro lado, para la neurona N2, con peso igual a 1, se
obtendría un resultado de 0, si se suma con el resultado anterior seguiría como estaba,
o lo que es igual 3.

3. En la tercera iteración, con X2 igual 0, sería el mismo resultado que la segunda iteración,
produciendo un valor de salida de N1 = 0 y N2 = 3.

4. Para finalizar con las iteraciones del primer layer, los valores obtenido se pasarían por
la tabla de la función sigmoid, produciendo un valor de resultado para N1 = 0 y N2 = 1
(como se puede observar en la tabla 4.5.2, para valores N1= “0000” y N2=”0011”) y un

83
estímulo de salida que comunica a la entidad que puede pasar a la siguiente capa con
valores de entrada previamente dichos.

5. En el segundo y último layer, para la primera iteración se le sumaría la BIAS asociada a


este layer, para la neurona N3, la cual tiene un valor de 01.

6. En la segunda iteración con valor de entrada N1 igual a 0, y, un peso de 1 asociada a la


neurona N3, se le sumaría al resultado un 1*0 = 0, quedando el resultado igual a 1.

7. Para la neurona N2, con valor de entrada 1, 1*1 = 1, se acumularía al resultado un 1 y


se obtendría que en N3 habría un valor de 2.

8. Por último, el valor de la función sigmoid asociado al 2, es un 1, por lo que, el resultado


final será un 1, provocando el final de la red neuronal asociado a ese valor y verificando
el correcto funcionamiento del proyecto.

Para la verificación completa, de forma análoga, se hace con las otras tres parejas de entrada,
obteniéndose el resultado esperado. En las siguientes figuras, se puede observar los distintos
resultados para todas las combinaciones de una puerta lógica XNOR de dos bits de entrada. El
valor de salida de la ANN es “OdataANN”, el flag de final del proceso de scoring es “FinishANN”,
el valor de entrada es “DATAIN y el resto son señales que contiene los pesos de cada neurona
y las salidas producidas en las distintas capas.

Figura 4.5.5. Resultado ANN (clasificando XNOR), para un valor de entrada “00”.

84
Figura 4.5.6. Resultado ANN (clasificando XNOR), para un valor de entrada “01”.

Figura 4.5.7. Resultado ANN (clasificando XNOR), para un valor de entrada “10”.

85
Figura 4.5.8. Resultado ANN (clasificando XNOR), para un valor de entrada “11”.

86
Capítulo 5: Planificación
En este apartado se expondrá la planificación del proyecto mediante el siguiente diagrama de

Gantt.

Figura 5.1. Planificación ANN en FPGAs (diagrama de Gantt).

Los tiempos son a nivel semanal, suponiendo que hubiese un analista-programador el cual se
encarga de los dos primeros hitos y un programador que se encarga de los otros dos, daría lugar
a unos presupuestos como los que se aprecian en la siguiente tabla (teniendo en cuenta que
los salarios son acordes al boletín oficial de la comunidad de Madrid (BOCM) de 2019):

Puesto Salario Tiempo Costes Costes Total


Base humanos materiales

Mensual

Analista 1.735,77 € 10 semanas (3 5.207,31 € PC 6.007,31


meses) €

Programador 1.608,95 € 12 semanas 4.826,85 € PC 5.626,85



(3 meses)

Tabla 5.1. Tabla de costes humanos y materiales del proyecto en base al tiempo.

87
Teniendo en cuenta que los costes materiales son solo un ordenador para poder realizar todo,
y que, las licencias del software usado para la simulación funcional son gratuitas. El proyecto
tendría un coste total de 11.634,16 €.

88
Capítulo 6: Aspectos éticos, sociales y
ambientales
En este capítulo se identificarán, describirán y analizarán los distintos aspectos sociales, éticos
y ambientales más relevantes tanto positivos como negativos.

6.1 Identificación
En primer lugar, para poder identificar estos aspectos de manera sencilla, hay que sesgar el
ámbito del proyecto. Este proyecto pertenece al ámbito de las tecnologías de la información y
comunicación, más específicamente, al diseño y modelado hardware.

Por otra parte, este proyecto tiene como finalidad investigación y análisis de viabilidad.

6.2 Descripción
Los principales problemas éticos/legales que se van a abordar son los siguientes:

1. Propiedad intelectual.

2. Privacidad.

Por otro lado, en lo que a los aspectos sociales se refiere:

1. LOPD (Ley Orgánica de Protección de Datos).

2. Seguridad y mantenimiento.

Por último, a nivel ambiental:

1. Agentes contaminantes.

2. Consumo eléctrico.

3. Reutilización o reciclaje.

6.3 Análisis
El análisis se realizará acorde al orden definido anteriormente.

89
Con lo que a propiedad intelectual se refiere, cabe destacar varios aspectos. La figura del tutor
de proyecto, las de las entidades involucradas en el desarrollo, y, la normativa UPM
(Universidad Politécnica de Madrid) como entidad para prevenir y detectar posibles plagios.

Conviene recalcar, que, cualquier información, documentación, idea, etcétera no realizado por
el autor y/o tutor del proyecto ha sido correspondientemente referenciada siguiendo la
normativa APA.

Por lo que a privacidad respecta, este proyecto tiene un fin de investigación y académico,
teniendo como consecuencia que, cualquier uso de datos, serán de carácter aleatorio o
totalmente anonimizados. Si bien es cierto que el alcance de este proyecto es, para ser usado
en un contexto de big data, en el caso de uso comercial se deberá seguir la normativa de la
LOPD.

La ley orgánica de protección de datos se define como “Ley Orgánica que tiene por objeto garantizar
y proteger, en lo que concierne al tratamiento de los datos personales, las libertades públicas y los
derechos fundamentales de las personas físicas, y especialmente de su honor e intimidad personal y
familiar” (BOE, 2000). Como se ha mencionado anteriormente, en lo que al tratamiento de

datos respecta, en este proyecto no se usarán ningún tipo de dato de carácter personal. Sin
embargo, si su uso fuese necesario, a los propietarios de dichos datos se les proporcionaría un
documento de consentimiento de cesión de datos, en el cual, constaría la concesión, uso y la
posibilidad de la eliminación total de los mismos.

Siguiendo con los aspectos sociales, con respecto a la seguridad y mantenimiento del sistema,
por un lado, la seguridad estará basada en el bit de anticopia que una FPGA nos proporciona
provocando que sea muy difícil el acceso al código embebido y los datos que circulan por el
mismo. También, destacar que el flujo de datos previo al acceso del sistema tendrá que cuidar
de la seguridad y protección de los datos, pero, el sistema no se responsabilizará de la seguridad
y protección en ese contexto.

Por otra parte, para el mantenimiento se han definido una seria de líneas futuras, en las cuales
se mejoraría el rendimiento y la usabilidad del sistema.

90
Para finalizar, en lo que al medioambiente se refiere, en este proyecto uno de los principales

puntos en los que se ha hecho hincapié, es en el consumo energético con respecto a las mimas

soluciones usando un hardware distinto. Por lo tanto, aunque, el sistema se nutre de energía

eléctrica (no autogenerada), el ahorro energético es importante. Por último, los agentes

contaminantes, al estar usando chips, PLCs, etc, son, por lo general, materiales como, plásticos,

silicio, aluminio y cobre. Como consecuencia, encadenándolo con la reutilización y reciclaje, en

el caso de desecho se tendrá que reciclar de manera coherente, ya que, muchos de los

materiales como el cobre, silicio se podrán reutilizar en nuevos sistemas.

91
92
Conclusiones
En primer lugar, todos los hitos han sido alcanzados satisfactoriamente de tal manera que
siguiendo el orden de hitos propuestos en el apartado de objetivo se concluye que:

1. Se han adquirido los conocimientos necesarios sobre redes neuronales artificiales, para
poder realizar dicho proyecto, mediante uso de documentación y cursos certificados, las
cuales se pueden encontrar en la bibliografía de este proyecto. También se han usado
conocimientos previos impartidos en el Grado de Ingeniería de Computadores de la
Universidad Politécnica de Madrid en asignaturas como Integración de Sistemas,
Sistemas Digitales, Mecatrónica, Sistemas con Microcontroladores y Microprocesadores
para dar una solución a nivel hardware y estructural apropiada al proyecto.

2. Con lo que a la solución propuesta respecta, se ha definido una arquitectura modular


por entidades, las cuales, se estructuran de menor a mayor (arquitectura bottom-to-
top) y mediante la unión de todas las entidades conforman la red neuronal artificial. Por
otro lado, se han usado varios algoritmos, funciones y herramientas como, el algoritmo
de propagación hacia delante, función de activaciones de neuronas (función sigmoid),
discretización de funciones y diagramas de flujo.

3. Para la realización del código fuente, se ha usado el lenguaje de descripción hardware


VHDL. El código realizado que implementa las distintas entidades cumple la solución
propuesta, la cual se comprueba mediante el uso de simulaciones funcionales de cada
entidad en el apartado de resultados.

4. Por último, se ha conseguido realizar un ejemplo de red neuronal artificial que realiza

un problema de clasificación de redes neuronales como puede ser clasificar la puerta

lógica XNOR.

93
94
Líneas Futuras
Este capítulo abordará las diferentes continuaciones del proyecto logrando cumplir el objetivo
del mantenimiento del sistema y ofreciendo la posibilidad de alargar la vida útil del mismo.

Cada línea futura distinta se tratará en apartados distintos, aunque, no todas serán dependiente
unas de otras, y, se podrán realizar en paralelo o en proyectos distintos. También, destacar, que
el orden de los sub-capítulos marca la prioridad que se ha elegido para continuar el proyecto.

Volcado del código en una FPGA


Esta línea futura es la continuación más básica del proyecto. Se la considera dependiente del
resto, ya que, en caso de que se realice algún cambio en el código del proyecto, afectará a las
siguientes líneas futuras.

En esta continuación, se tratarán aspectos básicos como el correcto funcionamiento del


proyecto (comprobando los requisitos funcionales descritos en el apartado de resultados) con
los retardos de propagación que se generan en la matriz de interconexión, y, distintos
componentes de una FPGA. También, aplicar los mismos ejemplos de los resultados del
proyecto pero, insertando los valores de entradas desde los I/O de la FPGA, comprobando que
son idénticos. A su vez, si fuese necesario, ajustar el código en caso de que se produjese alguna
condición de carrera.

Implementación del algoritmo “Backpropagation”


A lo largo de este proyecto se ha mencionado varias veces este algoritmo. La implementación
del mismo es independiente del resto, ya que, se puede realizar sin todo lo demás y luego
añadirlo al proyecto. Lo que se pretende es, mediante el uso del algoritmo de retro
propagación, recalcular los pesos asociados a las neuronas en vez de usar unos por defecto en
los que ya está minimizado el error a la función objetivo, como es el caso de este proyecto.

División de la estructura en varias FPGAs


Uno de los grandes problemas de esta solución es la cantidad de I/O y DSPs que se usan. Para
solventar este problema, se propone hacer uso de un cluster de FPGAs y la implementación de
95
un mecanismo para permitir la división de la estructura de la red en tantas partes como FPGAs
se dispongan. La conexión entre las distintas FPGAs se puede realizar de varias maneras o bien
usando una placa base con varios PCI, fibra óptica, etc.

Optimización de la solución propuesta


Ya se ha propuesto como mejorar a nivel de escalabilidad mediante el uso de más cantidad de
computo/hardware. Sin embargo, también se pueden realizar mejores propuestas a nivel de
código y estructura. Una de las propuestas es realizar un algoritmo de multiplicación de
matrices, previamente realizando un algoritmo de división de matrices basado en el algoritmo
de Strassen (Skiena, 1998). De esta manera se conseguirá una escalabilidad muy alta con pocos
recursos hardware. En primer lugar, se tendrían dos matrices, una para los valores de entrada
y otra para los pesos de las neuronas. A grandes rasgos, las matrices se multiplicarían y darían
un resultado de la red neuronal, pero, si la matriz es muy grande, ya que, hay mucha cantidad
de neuronas por capa en la red y no se dispone del hardware necesario para realizar está
multiplicación en un paso, se tendría que aplicar el algoritmo de Strassen en ambas matrices
dividiendo el problema en “trozos” más pequeños para así solventar este problema.

96
Glosario de Acrónimos
FPGA Field Programming Gate Array

IA Inteligencia Artificial

ANN Artificial Neural Network

CPU Central Processing Unit

GPUs Graphics Processing Unit

VHDL VHSIC Hardware Description Language

VHSIC Very High Speed Integrated Circuit

Big Data macrodatos, datos masivos, inteligencia de datos o datos a gran escala

Rectified Linear Unit Unidad Lineal Rectificada

Hyperbolic Tangent Tangente Hiperbólica

PEs Procesadores Elementales o Neuronas

97
IEEE Institute of Electrical and Electronic Engineers

RAM Random Access memory

ASICs Application-Specific Integrated Circuit

98
Bibliografía
Adarraga, P., & Zaccagnini, J. L. (1994). PSICOLOGÍA E INTELIGENCIA ARTIFICIAL. Madrid:
TROTTA.

Aguilera López, R. (2002). Tratado elemental de ajedrez. Madrid: Aguilera (Centro de Ajedrez
Internacional).

Alias, N. (15 de Septiembre de 2018). Slideshare. Obtenido de Slideshare:


[Link]
internet-of-things-and-big-data

AMD. (2019). AMD. Obtenido de AMD: [Link]

Atmel. (s.f.). microchip. Obtenido de [Link]


studio-7

BAIR. (Marzo de 2019). BAIR. Obtenido de BAIR: [Link]

BBVA. (2019). bbva. Obtenido de bbva: [Link]

Berkeley AI Research. (2019, Marzo). Berkeley AI Research. Retrieved from Berkeley AI


Research: [Link]

Berten. (Marzo de 2019). Berten. Obtenido de Berten:


[Link]
omparison_v1.[Link]

Blanco Pinto, L. M. (Marzo de 2019). slideplayer. Obtenido de slideplayer:


[Link]

BOE. (14 de Enero de 2000). [Link] Obtenido de [Link]


[Link]

Brian, W. K., & Dennis, N. R. (1978). The C programming language. Englewood Cliffs, New
Jersey: Prentice-Hall software series.

DATAprix. (2019). dataprix. Obtenido de dataprix: [Link]

99
Deepmind. (13 de Marzo de 2019). [Link]/. Obtenido de [Link]/:
[Link]
and-go/

DOD. (2019). defense. Obtenido de defense: [Link]

Doulos. (Abril de 2019). doulos. Obtenido de doulos:


[Link]

Elijah Charles, G. K. (Marzo de 2019). slideshare. Obtenido de slideshare:


[Link]
workloads

Escolano, F. (2003). Inteligencia artificial. Editorial Paraninfo.

Facebook. (Marzo de 2019). Facebook. Obtenido de Facebook: [Link]

Frontiers. (18 de Marzo de 2019). Frontiers. Obtenido de Frontiers:


[Link]
models-of-neural-information-processing

Funding Universe. (Marzo de 2019). Funding Universe Xilinx. Obtenido de Funding Universe
Xilinx: [Link]

Geoffrey, H. (1999). Unsupervised Learning and Map Formation: Foundations of Neural


Computation. Cambridge: MIT Press.

GitHub. (Marzo de 2019). GitHub. Obtenido de GitHub: [Link]

GitHub. (2019). GitHub. Obtenido de GitHub: [Link]

Google. (2019). Developers Google. Obtenido de Developers Google:


[Link]
neural-networks/anatomy?hl=es-419

Google. (25 de Marzo de 2019). Google. Obtenido de Google: [Link]

Hubel, D. H., & Wiesel, T. (2005). Brain and visual perception: the story of a 25-year
collaboration. Oxford: Oxford University Press US.

100
IEEE. (1988). IEEE Digital Libary. Obtenido de IEEE Digital Libary:
[Link]

IEEE. (Abril de 2019). IEEE. Obtenido de IEEE: [Link]

Intel. (2019). intel. Obtenido de intel:


[Link]

Jonathan, B., & Jonathan, M. B. (1 de Enero de 1988). Oxford Academic. Obtenido de Oxford
Academic: [Link]

Keras. (Marzo de 2019). Keras. Obtenido de Keras: [Link]

Lexikon, R. (2003). Medizin. Urban & Fischer .

Lise, G., & Ben, T. (2007). Introduction to Statistical Relational Learning. Maryland: MIT Press.

Lua. (Marzo de 2019). Lua. Obtenido de Lua: [Link]

Lua. (Marzo de 2019). LuaJIT . Obtenido de LuaJIT : [Link]

Mancebo, A. (Abril de 2019). slideplayer. Obtenido de slideplayer:


[Link]

Marr, B. (6 de Marzo de 2014). linkedin. Obtenido de linkedin:


[Link]
everyone-must-know/

Martínez, E. F., Rico, A. F., Romero, G. R., & Castro, D. L. (Junio de 2018).
[Link] Obtenido de [Link]
[Link]
f?sequence=1&isAllowed=y

Matich, D. J. (2001). Redes Neuronales: Conceptos Básicos y Aplicaciones. Rosario.

Microsoft. (Marzo de 2019). Microsoft. Obtenido de Microsoft: [Link]

Microsoft. (Marzo de 2019). Microsoft csharp. Obtenido de Microsoft csharp:


[Link]

101
Microsoft. (Marzo de 2019). Microsoft, cognitive-toolkit. Obtenido de Microsoft, cognitive-
toolkit: [Link]

Motorola. (2019). Motorola. Obtenido de Motorola: [Link]

Mxnet. (Marzo de 2019). Mxnet Apache. Obtenido de Mxnet Apache:


[Link]

National Instruments. (Marzo de 2019). National Instruments. Obtenido de National


Instruments: [Link]
[Link]

Ng, A. (30 de Octubre de 2018). Coursera. Obtenido de Coursera:


[Link]

Numpy. (Marzo de 2019). Numpy. Obtenido de Numpy: [Link]

Python. (20 de Febrero de 2019). Python. Obtenido de Python: [Link]

Skiena, S. S. (1998). The Algorithm Design Manual. New York: Springer Science & Business
Media.

Sobre Japón. (s.f.). sobre-japon. Obtenido de sobre-japon: [Link]


[Link]/2010/08/02/shogi-el-ajedrez-japones/

stockfishchess. (2 de Noviembre de 2008). stockfishchess. Obtenido de stockfishchess:


[Link]

Stroustrup, B. (1985).

Tensorflow. (25 de Marzo de 2019). Playground Tensorflow. Obtenido de Playground


Tensorflow: [Link]

Tensorflow. (25 de Marzo de 2019). Tensorflow. Obtenido de Tensorflow:


[Link]

Theano. (Marzo de 2019). Theano. Obtenido de Theano:


[Link]

Torch. (Marzo de 2019). Torch. Obtenido de Torch: [Link]

102
Université de Montréal. (Marzo de 2019). Université de Montréal. Obtenido de Université de
Montréal: [Link]

University of Toronto. (2019). University of Toronto. Obtenido de University of Toronto:


[Link]

usgo. (1997). usgo. Obtenido de usgo: [Link]

Verilog. (2019). verilog. Obtenido de verilog: [Link]

Wikipedia. (20 de Febrero de 2019). Wikipedia. Obtenido de Wikipedia:


[Link]

Wikipedia. (2019). Wikipedia. Obtenido de Wikipedia:


[Link]
[Link]

Wikipedia. (2019). Wikipedia. Obtenido de Wikipedia:


[Link]
Organizing_Map.png

Wikipedia. (Marzo de 2019). Wikipedia FPGAs. Obtenido de Wikipedia FPGAs:


[Link]
programmable_gate_array#/media/File:Altera_StratixIVGX_FPGA.jpg

Wikipedia. (Marzo de 2019). Wikipedia Logic Cell. Obtenido de Wikipedia Logic Cell:
[Link]

Wikipedia. (Abril de 2019). Wikipedia Switch Box. Obtenido de Wikipedia Switch Box:
[Link]

Wikipedia. (Abril de 2019). Wikipedia VHDL. Obtenido de Wikipedia VHDL:


[Link]

Wolfram. (2019). Wolfram. Obtenido de Wolfram:


[Link]

Xilinx. (2019). xilinx. Obtenido de xilinx: [Link]


devices/fpga/[Link]
103
Xilinx. (Marzo de 2019). Xilinx. Obtenido de Xilinx: [Link]

Xilinx. (Abril de 2019). Xilinx aplicaciones. Obtenido de Xilinx aplicaciones:


[Link]

Xilinx. (Abril de 2019). Xilinx I/O . Obtenido de Xilinx I/O :


[Link]

Xilinx. (Abril de 2019). Xilinx Virtex. Obtenido de Xilinx Virtex:


[Link]
[Link]#VUSP

Xilinx. (Abril de 2019). Xilinx Vivado. Obtenido de Xilinx Vivado:


[Link]

104

Common questions

Con tecnología de IA

FPGAs impact the adaptability of neural networks positively as they allow for reprogramming to accommodate changes in design. However, they may compromise performance compared to dedicated hardware due to additional logic used for reconfigurability, thus influencing energy efficiency and speed .

BBVA's use of machine learning to personalize customer interactions and enhance efficiency could substantially improve customer satisfaction and operational cost-effectiveness, potentially leading to stronger competitiveness and market presence across the more than 30 countries in which it operates .

BBVA may encounter several challenges such as ensuring data quality and privacy across diverse regulatory environments, integrating machine learning models with existing systems, and aligning machine learning strategies with varied market needs and customer expectations in different countries .

Open-source frameworks like Theano and Torch offer significant benefits for deep learning model development by providing extensive collections of machine learning algorithms, supporting multiple languages, and enabling scalability across different hardware configurations, which fosters collaborative improvements and adaptation in the community .

BBVA aims to enhance customer service by using machine learning to simulate the actions an employee would take with a client, thereby providing tailored services efficiently through machines instead of human resources .

TensorFlow allows users, especially those new to programming, to easily implement neural networks by providing a high-level Python API that abstracts complex implementations, making the process simpler and more intuitive .

The 'Neuron Top' entity accumulates input values, applies biases, and computes outputs by integrating inputs with weights, completing the propagation of signals as part of the neural network's forward propagation. It ensures precise data handling and processing at each node level in the network .

FPGAs are advantageous over ASICs in that they are reprogrammable, allowing cost and design flexibility. However, FPGAs have higher energy consumption and lower performance compared to ASICs, due to the overhead of reconfigurability .

Languages like VHDL are crucial for designing hardware-specific models because they allow for precise control over the architecture and functioning of digital circuits, facilitating the implementation of neural networks on programmable logic devices like FPGAs, where detailed customization of hardware function and connectivity is required .

The 'Layer Generation' entity is responsible for creating layers in a neural network by initializing the weights and controlling the flow of data across neurons. It defines the number of bits for data buses and provides input and output ports to manage data flow, thereby supporting the propagation and processing of inputs through the network .

También podría gustarte