Está en la página 1de 91

Descripción de

circuitos digitales
en VHDL
Grado en Ingeniería Informática

Trabajo Fin de Grado


Autor:
Jose Luis Mateo Pastor
Tutor/es:
Ángel Grediaga Olivo

Julio 2022
Índice general

1. Marco teórico y objetivos


1.1. Temática del trabajo . . . . . . . . . . . . . . . . . . . . . . . . .
1.2. Objetivos del trabajo . . . . . . . . . . . . . . . . . . . . . . . . .
1.3. Metodologı́a a seguir . . . . . . . . . . . . . . . . . . . . . . . . .

2. Sección 1: Introducción a la electrónica digital y el diseño de


hardware lógico
2.1. Nociones básicas de electrónica general . . . . . . . . . . . . . . .
2.1.1. ¿Qué es y para qué sirve la electrónica? . . . . . . . . . .
2.1.2. ¿Cuales son las principales ramas de la electrónica? . . . .
2.1.3. ¿Por qué elegir la electrónica digital en vez de la analógica?
2.1.4. Aplicaciones de la electrónica digital . . . . . . . . . . . .
2.1.5. Conceptos previos para introducirse a la electrónica digital
2.2. Introducción a la electrónica digital . . . . . . . . . . . . . . . . .
2.2.1. Semiconductores y transistores . . . . . . . . . . . . . . .
2.2.2. Puertas lógicas . . . . . . . . . . . . . . . . . . . . . . . .
2.2.3. Circuitos lógicos . . . . . . . . . . . . . . . . . . . . . . .
2.2.4. Circuitos combinacionales . . . . . . . . . . . . . . . . . .
2.2.5. Circuitos secuenciales . . . . . . . . . . . . . . . . . . . .
2.3. Diseño de hardware digital . . . . . . . . . . . . . . . . . . . . . .
2.4. FPGA’s . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.5. Estado del arte . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3. Sección 2: Desarrollo de circuitos combinacionales básicos con


VHDL
3.1. Introducción y contexto . . . . . . . . . . . . . . . . . . . . . . .
3.2. Puerta XOR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3. Puertas NAND y NOR . . . . . . . . . . . . . . . . . . . . . . . .
3.4. Multiplexor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.5. Semisumador y sumador completo . . . . . . . . . . . . . . . . .

4. Sección 3: Desarrollo de circuitos combinacionales avanzados


con VHDL, diseño e implementación de una ALU
4.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
ÍNDICE GENERAL

4.2. Resta binaria y Complemento a 2 . . . . . . . . . . . . . . . . . .


4.3. Implementación del sumador/restador de 8 bits en VHDL . . . .
4.4. Multiplexores con entrada Enable . . . . . . . . . . . . . . . . . .
4.4.1. La sentencia Process . . . . . . . . . . . . . . . . . . . . .
4.5. Implementación de la ALU y su lógica de control . . . . . . . . .
4.6. Reimplementación de la ALU . . . . . . . . . . . . . . . . . . . .

5. Sección 4: Desarrollo de circuitos secuenciales básicos con VHDL


5.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2. Flip-Flops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.3. Registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.4. Memorias ROM y RAM . . . . . . . . . . . . . . . . . . . . . . .
5.5. Contadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

6. Sección 5: Desarrollo de máquinas de estados con VHDL


6.1. Introducción y conceptos clave . . . . . . . . . . . . . . . . . . .
6.2. Tipos enumerados . . . . . . . . . . . . . . . . . . . . . . . . . .
6.3. Autómata 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.4. Autómata 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.5. Autómata 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.6. Autómata 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7. Proyecto Final: Implementación de una UART


7.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.2. Divisor de frecuencia . . . . . . . . . . . . . . . . . . . . . . . . .
7.3. Diseño del transmisor . . . . . . . . . . . . . . . . . . . . . . . .
7.3.1. Configuración de la entidad . . . . . . . . . . . . . . . . .
7.3.2. Configuración de la arquitectura . . . . . . . . . . . . . .
7.3.3. Definición del sistema de control . . . . . . . . . . . . . .
7.3.4. Implementación del transmisor . . . . . . . . . . . . . . .
7.4. Diseño del receptor . . . . . . . . . . . . . . . . . . . . . . . . . .
7.4.1. Configuración de la entidad . . . . . . . . . . . . . . . . .
7.4.2. Configuración de la arquitectura . . . . . . . . . . . . . .
7.4.3. Definición del sistema de control . . . . . . . . . . . . . .
7.4.4. Implementación del receptor . . . . . . . . . . . . . . . .
Marco teórico y objetivos.

1. Temática del trabajo


La temática de este trabajo se enmarca dentro del prototipado de circuitos digitales y el desarrollo
en sistemas FPGA. Estos sistemas se pueden configurar mediante distintos lenguajes, pero la alter-
nativa que se va a emplear aquı́ será la del lenguaje VHDL. Para enfocar correctamente este trabajo
debemos tener muy presente la idea de que VHDL no es un lenguaje de programación, si no que es lo
que se conoce cómo un lenguaje de descripción de hardware.

Vamos a explicar un poco más lo que es VHDL para que quede claro antes de entrar en materia.
VHDL es un lenguaje de especificación definido por el Instituto de Ingenieros Eléctricos y
Electrónicos (IEEE) que se utiliza para describir sistemas digitales, y es muy útil para el diseño de
circuitos. Existen otros lenguajes que pueden cumplir el mismo propósito, cómo Verilog, aunque este
tiene un nivel de abstracción mayor, es decir, nos permite describir sistemas de manera más ((sencilla)),
pero nos da menos control sobre lo que hacemos, ya que no nos permite interactuar con detalles de
más bajo nivel.

Pero vamos a retroceder un poco, hemos dicho que estos lenguajes sirven para desarrollar e im-
plementar sistemas digitales sobre plataformas FPGA, ¿y de que tratan estos FPGA? No vamos a
profundizar mucho en ello ahora mismo, ya que se hablará más sobre el tema en posteriores sec-
ciones, pero vamos a presentar de manera superficial la idea. Los FPGA son unos dispositivos
que se pueden reconfigurar prácticamente de infinitas maneras para construir cualquier
tipo de circuito que se desee. La manera en que estos sistemas realizan estos reajustes es mediante
cambios en las interconexiones de sus recursos lógicos. Por lo tanto, la única limitación que habrá en
lo que se puede o no se puede hacer con un sistema FPGA serán los recursos lógicos de los que dispon-
ga, ya que dichos recursos lógicos los podremos combinar y configurar de la manera que queramos.

Cómo hemos mencionado antes, VHDL no es un lenguaje de programación, y pensar en él cómo
tal nos puede llevar a incurrir en un sinfı́n de errores. Debemos tener siempre presente que lo que
estamos haciendo es describir circuitos, y por lo tanto gran parte de lo que se va a describir se
ejecutará de manera concurrente, a pesar de que las sentencias que lo describen puedan parecer
secuenciales. Es precisamente este paradigma concurrente, uno de los principales factores que llevan
a cometer errores a los programadores informáticos que se inician en VHDL.

1
3 METODOLOGÍA A SEGUIR

2. Objetivos del trabajo


El objetivo de este trabajo, de carácter didáctico, es servir como una guı́a para el apren-
dizaje de descripción de sistemas digitales en VHDL. Para ello se van a presentar todos
los conceptos importantes de manera teórica ası́ como práctica. Se mostrarán diferentes trozos de
código que resolverán los problemas que se traten a lo largo del trabajo, y se relacionarán dichos
códigos con los conceptos teóricos pertinentes. Se revisarán conceptos que si bien pueden pare-
cer muy básicos, son fundamentales para comprender bien lo que se va a hacer, por lo que
lo que se va a dar por sabido es más bien poco. El lector tan sólo deberá conocer los conceptos más
básicos de la lógica digital, ası́ como los sistemas binario y hexadecimal. Más allá de esto, no se em-
plearán conceptos más complicados sin explicarlos aunque sea brevemente.

Lo que se busca es que para cuando el lector haya finalizado este material, haya adquirido una
buena base sobre la descripción en VHDL, y soltura para implementar sistemas basados en FPGA.
También es importante que se adquiera una idea general de todas las aplicaciones prácticas de es-
ta tecnologı́a, y del papel de esta en la industria actualmente, ya que es algo relativamente ((moderno)).

3. Metodologı́a a seguir
La metodologı́a a seguir consistirá en dos fases:

En la primera fase, la cuál será la mayor parte de este trabajo, se explicarán todas las bases
y se mostrarán las aplicaciones prácticas de éstas, explicando las distintas implementa-
ciones que se van a realizar, y aumentando gradualmente la complejidad de éstas. Lo que vamos
a hacer es ir construyendo sistemas cada vez más complejos, comenzando por las unidades más
simples y construyendo en base a estas las más complejas. Empezaremos con una introducción
a la electrónica digital en general y luego exploraremos el diseño de circuitos combinacionales y
secuenciales de distinta complejidad.

En la segunda fase, la cual se verá en el capı́tulo final del trabajo, se pondrán todos los conceptos
aprendidos a trabajar, implementando un sistema UART, lo cual ya se puede considerar un
desarrollo bastante completo. Para esto deberemos haber interiorizado bien todo lo relacionado
con diseño combinacional y secuencial que se presenta en la fase 1.

2
Sección 1: Introducción a la electrónica digital y el
diseño de hardware lógico.
José Luis Mateo Pastor

1. Nociones básicas de electrónica general

1.1. ¿Qué es y para que sirve la electrónica?

La electrónica es una rama de la fı́sica aplicada que estudia el comportamiento de los elec-
trones. Actualmente una de las principales aplicaciones de esta disciplina es el diseño y estudio de
circuitos eléctricos, los cuales son fundamentales en muchas ramas de la ingenierı́a, especialmente en
informática y telecomunicaciones.

Hoy en dı́a, prácticamente toda la tecnologı́a que utilizamos emplea la electrónica. Cabe resaltar
que, normalmente, cuando se habla de electrónica en el ámbito de la ingenierı́a, se asume que se va a
trabajar con circuitos eléctricos de bajo voltaje e intensidad, normalmente con corriente continua(no
confundir con señales continuas).

Además, en la electrónica moderna, la tecnologı́a de semiconductores(la cual es relativamente


”nueva”) juega un papel fundamental. Se hablará un poco más sobre los semiconductores más
adelante.

Figura 1: No fue hasta mediados del siglo pasado cuando se comenzaron a sustituir estas rudimentarias
válvulas de vacı́o por transistores basados en semiconductores. Fuente: https://es.wikipedia.org

1.2. ¿Cuales son las principales ramas de la electrónica?

Sin duda el campo de la electrónica es un campo inmenso, pero podrı́amos hacer una diferencia-
ción a grandes rasgos entre dos ámbitos muy importantes en los que se aplica la electrónica:

1
1 NOCIONES BÁSICAS DE ELECTRÓNICA GENERAL

Electrónica Analógica

Electrónica Digital

La principal diferencia entre estas dos variantes es que la vertiente analógica trabaja con señales
continuas, mientras que la vertiente digital emplea señales discretas. Esto trae consigo múltiples
implicaciones que hay que tener en cuenta:

En la electrónica analógica se deben realizar cálculos matemáticos complejos cuando se quieren cons-
truir circuitos completos. Se deben tener muy en cuenta los valores de corriente, voltaje y resistencia
y muchas veces se deben emplear herramientas matemáticas para comprender el comportamiento de
la corriente alterna en los distintos elementos del circuito. En estos circuitos, las tareas de diseño
suelen implicar una baja abstracción, lo cual implica que se trabaja directamente con las unidades
fundamentales de los circuitos eléctricos: condensadores, resistencias, bobinas, transistores...

Por otra parte, en la electrónica digital, se trabaja con un nivel de abstracción mucho más alto. Ya
no se trata de centrarse tanto en el cómo si no en el qué. Lógicamente, al tratar con señales discretas, los
cálculos matemáticos se simplifican bastante. Nuestras señales en los circuitos digitales siempre
tendrán dos valores posibles: 0 o 1. Esto se puede interpretar como diferentes rangos de voltaje
en el ámbito del circuito analógico subyacente, pero como ya se ha mencionado, aquı́ no se va a tener
en cuenta ninguno de estos aspectos de bajo nivel, lo único que nos va a importar es el flujo de ceros
y unos que circula por los circuitos.

1.3. ¿Por que elegir la electrónica digital en vez de la analógica?

No se trata de elegir en el sentido de que una sea mejor que otra, se trata de que cada una tiene
unos casos de uso para los que es más adecuada.

Como ya se ha comentado antes, al final, toda la electrónica es analógica, por la propia naturaleza
((continua)) de la electricidad(a nivel macroscópico, que es el que nos interesa en ingenierı́a, la fı́sica
cuántica tiene un punto de vista mucho más exótico respecto a esto).

Pero cuando hablamos de trabajar con electrónica digital, lo que debemos entender es
que nos estamos refiriendo más a un enfoque de trabajo y paradigma tecnológico que a una
forma de entender como se comportan los electrones.
Por lo tanto, podemos liberarnos de todas las complejidades que implican los circuitos electrónicos
((en crudo)) y centrarnos en construir soluciones discretas basadas en bloques básicos de funcionalidades
ya implementados sobre dichos circuitos.
Nosotros casi nunca tendremos que calcular corrientes ni voltajes, nos podemos olvidar de todos
los cálculos engorrosos que incluyen ecuaciones diferenciales, números complejos...

2
Universidad de Alicante: Grado en ingenierı́a informática.

Tan sólo deberemos trabajar con lógica binaria, para la cual hay un marco de trabajo
primordial, el álgebra de Bool. Ya se verá esto más adelante.

Figura 2: Cálculos fasoriales en un circuito analógico(izquierda) vs cálculos booleanos en un circuito


digital(derecha). Fuente: http://hyperphysics.phy-astr.gsu.edu

1.4. Aplicaciones de la electrónica digital

Hemos dicho que en electrónica digital se trabaja con ceros y unos, es decir, las señales van
a estar en dos estados distintos, son señales binarias. No es casualidad, por lo tanto, que toda la
tecnologı́a informática actual trabaje en base a esos sistemas binarios. En realidad, existe un tercer
estado que se usa también en electrónica digital, y es el de alta impedancia, el cual no representa ni
un 0 ni un 1, si no la ausencia de señal. Es crucial cuando se desea que una parte de un sistema no
conduzca ningún tipo de información.

La gran revolución de la tecnologı́a moderna ha sido el transistor, el cuál fue el que aceleró enor-
memente el desarrollo de la tecnologı́a digital, y sin el cual la tecnologı́a serı́a muy diferente a lo que
conocemos hoy en dı́a.

A grosso modo, un transistor CMOS no es más que un ((interruptor)), que deja pasar o bloquea
la corriente según se le indique. Más adelante se explicará esto con un poco más de detalle.
La base de toda nuestra tecnologı́a digital son estos transistores, y dado que la base del hardware
informático es la electrónica digital, podemos decir que la informática moderna depende fuertemente
de la evolución en las caracterı́sticas y prestaciones de estos transistores.
Una vez más, cuando trabajemos con componentes digitales, no nos vamos a preocupar por como
funcionan estos transistores a bajo nivel, pero es importante tener unas nociones básicas.
Volviendo a cuales son las aplicaciones de la electrónica digital, dado que todo se está infor-
matizando cada vez más hoy en dı́a, el número de sistemas que se basan en esta disciplina crece
continuamente.

En última instancia, podrı́a decirse que todo lo que emplea sistemas informáticos funciona con
electrónica digital principalmente.

3
1 NOCIONES BÁSICAS DE ELECTRÓNICA GENERAL

Entre los sistemas en los que podemos encontrar circuitos digitales tenemos:

Microcontroladores y microprocesadores.

Memorias.

Buses de comunicación.

Sensores.

Elementos de control.

Combinando estas tecnologı́as se puede desarrollar cualquier tipo de sistema digital que se desee.

1.5. Conceptos previos para introducirse a la electrónica digital

Antes de entrar en detalle sobre el funcionamiento de los circuitos digitales y terminar diseñando
nuestros propios circuitos, debemos asimilar ciertos conceptos y terminologı́a básica:

1. Las unidades funcionales fundamentales de estos circuitos serán las puertas lógicas. Estos elemen-
tos son los que nos permitirán trabajar con la lógica binaria.

2. La unidad mı́nima de información es el bit, al estar trabajando con señales discretas, las
transmisiones dentro de estos circuitos se pueden fragmentar en ”paquetes”más pequeños que
serán estos bits. La palabra bit es una abreviatura para el término ”binary digit”, dı́gito binario
en español, y como su nombre indica se trata de un solo elemento de señal en uno de sus posibles
estados.

3. Los componentes de los circuitos pueden reaccionar de distinta manera a las señales binarias
dependiendo de los resultados que se quieran conseguir. Una señal en el estado 1 puede ac-
tivar o desactivar un componente dependiendo de si el sistema utiliza lógica positiva
o lógica negativa. Con la lógica negativa, las señales se consideran en su estado de activación
cuando están en cero. Si no se especifica lo contrario, asumiremos que trabajamos con lógica
positiva.

4. No se pueden procesar señales analógicas con circuitos digitales directamente, se requiere una
conversión previa. Por ello, ciertos componentes como los sistemas de audio de los ordenadores
incluyen un DAC(conversor digital-analógico) para transformar señales digitales a analógi-
cas, igual que con un ADC se pueden transformar señales analógicas en digitales.

5. Como ya hemos mencionado antes, al trabajar en digital nosotros vamos a ignorar detalles
subyacentes como los voltajes y las intensidades operativas, pero debemos ser conscientes de que
nuestros niveles lógicos en el contexto digital siempre se traducen en rangos de voltajes en la
base analógica, los cuales pueden variar dependiendo de muchos factores. Pero eso, como ya se
ha dicho, no nos tiene que importar demasiado, solo debemos saber que para indicar un nivel
lógico bajo(un 0) se utiliza el mı́nimo voltaje del rango establecido, y para un nivel lógico alto(un

4
Universidad de Alicante: Grado en ingenierı́a informática.

1) se utiliza el máximo voltaje. Podrı́amos encontrar un circuito en el que el 0 sean 0 Voltios


y el 1 sean 5 Voltios, lo cual es algo habitual. En la actualidad, se trabaja mucho también con
tensiones de 3.3 V.

6. Cuando se habla de alta abstracción se está hablando de como se puede trabajar con un sistema
sin tener que atender a las complejidades de su más bajo nivel. En el caso de los circuitos
eléctricos digitales, nos abstraemos bastante porque no tenemos que tomar en consideración
parámetros fı́sicos como las resistencias empleadas ni realizar cálculos sobre las corrientes que
fluyen a través de ellos.

Figura 3: Niveles de abstracción de los circuitos digitales. Nosotros trabajaremos con los tres niveles
más altos de abstracción: Puertas,Módulos y Sistema. Fuente: https://programmerclick.com

2. Introducción a la electrónica digital


Ahora vamos a realizar una breve introducción a los conocimientos básicos que se requieren para
diseñar sistemas digitales. Lo que aquı́ se va a presentar se irá completando a medida que se avance
en las aplicaciones prácticas respectivas, a lo largo de este trabajo.

Puede que esta introducción parezca muy teórica, pero en las siguientes capı́tulos todo irá cobrando
más sentido, a medida que se presenten diversas consideraciones sobre los sistemas que se van a diseñar.

2.1. Semiconductores y transistores

Los semiconductores son unos materiales que poseen la capacidad de actuar como ais-
lantes o conductores eléctricos según se den determinadas condiciones. No es el objetivo de este
trabajo entrar en este tipo de concreciones, pero es importante saber que los semiconductores son los
materiales que se utilizan para construir los transistores.

Respecto a los transistores, si que es importante comprender su funcionamiento y caracterı́sticas


al menos funcionalmente.

5
2 INTRODUCCIÓN A LA ELECTRÓNICA DIGITAL

Figura 4: Encapsulados tı́picos y sı́mbolos de transistores. Fuente: https://startingelectronics.org

Existen distintos tipos de transistores y algunos funcionan ligeramente diferente, pero


la filosofı́a es muy similar entre todos ellos. Hay un elemento de control, una entrada y una salida. El
elemento de control determinará si la corriente que circula por la entrada se transmite a la
salida.

En el caso del transistor situado a la izquierda en la figura 4, se trata de un transistor de unión


bipolar, en el cual si hay corriente en la base, el colector y el emisor se ((conectan)), lo cual permite
circular a la corriente de entrada. En el caso de que no se alimente corriente a la base, no habrá flujo
eléctrico a través del transistor.

El transistor de la derecha es un poco diferente, y es más actual. Se trata de un transistor MOS-


FET, de la familia lógica CMOS. A dı́a de hoy, es la tecnologı́a que más se emplea, especialmente
en circuitos integrados, en los que es necesario un nivel de miniaturización muy grande. Este nivel
de miniaturización se puede llevar a cabo gracias a una técnica llamada fotolitografı́a, en la cual se
((imprimen)) los componentes de los chips sobre obleas de un material semiconductor(normalmente
silicio) mediante la inyección de diferentes sustratos quı́micos sobre las obleas y su configuración me-
diante haces de luz proyectados sobre unas placas denominadas fotomáscaras, las cuales contendrán el
patrón a imprimir sobre el sustrato. Hoy en dı́a podemos encontrar miles de millones de transistores
en un pequeño chip, gracias a esta integración a gran escala, también llamada VLSI(Very Large Scale
Integration).

El nombre de CMOS proviene de Complementary Metal-Oxide Semiconductor(Semiconductor Com-


plementario de Óxido Metálico) y como su nombre indica, se trata de una unión complementaria de
dos tipos de transistores, los PMOS y los NMOS (ambos basados en tecnologı́a MOSFET), los cuales
por separado son imperfectos, pero combinados funcionan muy bien, ya que los MOSFET (transisto-
res de efecto de campo), ofrecen ciertas prestaciones que los hacen ideales para construir chips. Por
ejemplo, son más fáciles de fabricar a escala que los BJT(Bipolar Junction, el otro tipo de transistor
que se ha mencionado antes) y generan menos ruido eléctrico, entre otras ventajas.

6
Universidad de Alicante: Grado en ingenierı́a informática.

En los transistores MOSFET, pasamos de tener los terminales colector, base y emisor de los BJT
a tener los terminales drenador,puerta y surtidor. Además,los MOSFET utilizan una entrada de
control activada por voltaje(a diferencia de la activación por corriente que tenı́amos en los BJT).
A pesar de todo, la filosofı́a sigue siendo la misma que en el resto de transistores, como ya se ha
comentado: el transistor MOSFET dejará pasar la corriente entre el terminal fuente y el de drenaje
cuando se aplique una tensión al terminal puerta.

Aunque parezca mentira, con estas reglas tan sencillas, los transistores nos permiten crear casi
cualquier dispositivo que se nos ocurra.

2.2. Puertas lógicas

Como ya se ha mencionado antes,las puertas lógicas son los componentes básicos de todos los
circuitos digitales. Vamos a explicar en detalle las más importantes, el resto de puertas lógicas más
avanzadas se pueden construir todas en base a estas:

2.2.1. Puertas AND

Figura 5: Puerta AND y diagramas RTL. Fuente: http://hyperphysics.phy-astr.gsu.edu

Como se puede ver en la imagen de la primera fila a la izquierda en la figura 5, si interpretamos


el diagrama con transistores BJT, esta puerta lógica sólo deja pasar la electricidad cuando todos sus
transistores reciben corriente en la base, y por lo tanto esta fluye hacia la salida a través de los emi-
sores. Si cualquiera de los transistores bloqueara la corriente, esta no llegarı́a a la salida, ya que están

7
2 INTRODUCCIÓN A LA ELECTRÓNICA DIGITAL

todos en serie(el emisor de un transistor se encadena con el colector del siguiente). En este ejemplo
concreto, con una puerta de 2 entradas(A y B), tenemos que activar dos transistores para que la
corriente llegue desde la toma de alimentación hasta la salida(Out).

Esto aplicado a la electrónica digital, se traduce en que en una puerta AND tendremos una salida
activa cuando todas las entradas sean también activas(esto utilizando lógica positiva), como se puede
observar en la tabla de verdad mostrada.

No se va a explicar el circuito con transistores MOSFET porque eso queda fuera del propósito de
esta sección introductoria. Aquı́ hay que destacar, que con lógica CMOS, no se ha construido una
puerta AND directamente, si no que se ha colocado un inversor a la salida de una puerta NAND(No
AND), la cual es a su vez equivalente a colocar otro inversor a la salida de una puerta AND corriente.
Ya se hablará de las puertas NAND en otras secciones.

Para finalizar, dado que en el mundo digital solo se usan unos y ceros, las puertas lógicas no iban
a ser una excepción, por lo que podemos generalizar una puerta AND como un dispositivo de N
entradas y una salida, la cual se pondrá a uno si todas sus N entradas están a uno también.

2.2.2. Puertas OR

Figura 6: Puerta OR y diagramas RTL. Fuente: https://mielectronicafacil.com

Este circuito es un poco más complicado de analizar en su diagrama RTL(Resistor-Transistor Lo-


gic) que el anterior, pero vamos a explicarlo para que se comprenda.

Vamos, como en el caso anterior, a analizar la base de su funcionamiento con el circuito ((clási-
co))(el que no usa lógica CMOS, en la imagen de la izquierda). Cabe decir que este circuito utiliza una
tecnologı́a que hoy en dı́a ha quedado obsoleta, ya casi no se usa la lógica Transistor-Transistor. Lo
que podemos observar aquı́(aunque puede que no parezca evidente a primera vista) es que esta vez,
todos los transistores están en paralelo en el circuito. Ahora, los transistores no se encadenan entre

8
Universidad de Alicante: Grado en ingenierı́a informática.

sı́, si no que todos tienen conexión directa de sus emisores con la salida, y conexión directa de sus
colectores con la toma de alimentación. Por lo tanto, basta con que un solo transistor deje pasar la
corriente para que tengamos señal en la salida.

En este caso, es fácil deducir (ayudándonos de la tabla de verdad), que para una puerta lógica OR
de N entradas, la salida estará activa(nivel lógico a 1) cuando cualquiera de sus N entradas
estén activas. Podrı́a verse esta puerta OR como una especie de suma entre dos señales de 1 bit,
aunque esto no es del todo preciso, ya que para el caso de que las dos entradas sean 1 la salida obtenida
serı́a 1, lo cual difiere de la suma binaria propiamente dicha, que serı́a 0(el 1 serı́a el acarreo de la
suma, es decir, lo que me ”llevo”).

Por ello existe una versión más exhaustiva de esta lógica de puerta, la puerta XOR, la cual es una
puerta compuesta de otras puertas mas sencillas, lo veremos en apartados posteriores, ahora estamos
viendo las puertas básicas.

2.2.3. Puertas NOT

Figura 7: Puerta NOT y diagramas RTL. Fuente: https://mielectronicafacil.com

Probablemente la puerta más sencilla de todas, y una de las más importantes también, ya que
combinándola con las antes mencionadas vamos a poder derivar la mayorı́a de puertas compuestas.
Este dispositivo lo único que hace es invertir la señal lógica que se le pase a la entrada.
Es decir, si recibimos un cero, sacaremos un uno, y viceversa.

Interpretar el diagrama RTL en este caso es sencillo: para la versión con tecnologı́a TTL(imagen a la
izquierda), podemos observar que cuando no tenemos corriente en la entrada(A), la base del transistor
se mantiene inactiva, por lo que la corriente de alimentación no fluye desde el colector hasta el emisor,
si no que se desvı́a hacia la salida Y(lo que en digital se interpreta como un nivel alto o señal lógica a 1).

Si aplicamos corriente en la entrada, el transistor dejará pasar los electrones desde alimentación a
tierra, y no tendremos señal en la salida(lo que se interpreta como un 0).

9
2 INTRODUCCIÓN A LA ELECTRÓNICA DIGITAL

Para la versión con tecnologı́a CMOS(imagen al centro), la interpretación es muy similar, en este
caso lo que se invertirá será la señal que reciba el terminal puerta.

2.3. Circuitos lógicos

Combinando las puertas lógicas de manera inteligente podemos crear bloques de funcionalidades
muy convenientes. Recordemos que dichas puertas son la base de todo en los circuitos digitales, con
ellas se puede implementar cualquier tipo de lógica, ya sea simple o compleja. Hay dos tipos de circuitos
lógicos, que se explicarán a continuación.

Figura 8: Diagrama lógico de una ALU muy sencilla. Fuente: https://www.101computing.net

2.4. Circuitos combinacionales

Vamos a hablar un poco de los dos tipos principales de circuitos que encontramos en el mundo
digital: los combinacionales y los secuenciales.

En primer lugar, vamos a definir lo que es un circuito combinacional, ya que los secuenciales se
construyen en base a estos. Un circuito combinacional es un circuito formado por distintas puertas
lógicas que realizan operaciones que arrojarán una salida dadas unas entradas determinadas. Es decir,
el valor de sus salidas en un instante t dependerá únicamente del valor de las entradas en ese instan-
te. En ningún caso intervienen estados anteriores de las entradas o las salidas, las salidas del circuito
en tiempo T son función exclusiva de las entradas en T.

Figura 9: Diagrama particular de un circuito combinacional. Fuente: https://web.unican.es

10
Universidad de Alicante: Grado en ingenierı́a informática.

Ejemplos de circuitos combinacionales serı́an sumadores, multiplexores, comparadores, etc...

Estudiaremos estos circuitos en secciones posteriores y aprenderemos a diseñarlos e integrarlos en


sistemas mas complejos.

2.5. Circuitos secuenciales

Ampliando las capacidades de los circuitos combinacionales tenemos los circuitos secuenciales. Se
tratan de circuitos en los que la salida en un instante dado ya no solo depende de la combinación de las
entradas en ese instante, sino también de la historia de las mismas, es decir, los estados anteriores del
circuito. Esta dinámica se introduce con la adición de un elemento crucial para los sistemas digitales,
las memorias.

Al tener ahora elementos de memoria, podemos almacenar resultados y estados en un instante T,


los cuales pueden cambiarse en un instante T+1 en base a las reglas de transición que se definan.

Figura 10: Diagrama general de un circuito secuencial. Fuente: http://czzsjd2.blogspot.com

Como podemos ver en la figura, se trata de usar lo que ya tenı́amos en los circuitos com-
binacionales y añadir la capacidad de ((recordar)) distintos estados. Podemos realimentar un
estado posterior con las entradas y las salidas de un estado anterior, esto depende de las especifica-
ciones del diseño, cada circuito construirá sus condiciones de excitación de una manera determinada.

3. Diseño de hardware digital


Combinando los dos tipos de circuitos que hemos presentado previamente, se pueden
crear todo tipo de sistemas digitales: procesadores, sistemas de transmisión, memorias...

Para diseñar circuitos digitales, lo más importante es seguir una lógica sólida, de la misma mane-
ra que cuando se desarrolla software. Una vez se comprenden todos los fundamentos de las puertas
lógicas y su lógica correspondiente(álgebra de Bool, leyes de DeMorgan...), cualquier sistema avanza-
do tratará de ir combinando todas las piezas básicas para terminar construyendo bloques más grandes.

11
4 FPGA’S

Un enfoque bastante conveniente a la hora de diseñar estos sistemas, es el principio de ”Divide


y vencerás”, el cual trata de subdividir un problema grande en muchos problemas pequeños.

Por ejemplo, si quisiéramos diseñar una unidad Aritmética para un procesador, una estrategia
inteligente serı́a enfocar el diseño en las distintas partes que componen la unidad, en vez de tratarla
como un todo. Podrı́amos empezar diseñando los sumadores necesarios y luego pasar a las operaciones
lógicas y seguir con las siguientes funcionalidades, centrándonos en una sola en cada momento.

A continuación se hablará de las distintas posibilidades disponibles para realizar estos diseños.
Estas soluciones se van a presentar sin profundizar mucho en ellas, ya que en este trabajo nos vamos
a centrar en la implementación sobre estas plataformas, más que en la plataforma subyacente en sı́.

4. FPGA’s

Hay muchas maneras de diseñar y construir circuitos y sistemas digitales que se adap-
ten a nuestras necesidades. Hasta no hace mucho, cuando una compañı́a querı́a desarrollar un chip
con unas funcionalidades concretas, tenı́a que invertir mucho tiempo y dinero en fabricar el circuito de
ese chip ”desde cero”, con el riesgo añadido de que cualquier fallo en el proceso de diseño o desarrollo
podı́a costar pérdidas millonarias y meses de trabajo para solventar.

Por ejemplo, si se querı́a diseñar un circuito que no fuera de propósito general(especı́fico para una
tarea concreta), se debı́a optar por fabricar un ASIC(circuito integrado de aplicación especı́fica), y
habı́a que lidiar con toda la complejidad inherente al diseño de circuitos eléctricos, prácticamente se
tenı́a que determinar todo el esquemático del circuito desde el más bajo nivel, no se trabajaba con
ningún tipo de abstracción.

Esto cambió hace ya algunos años, con la evolución de la tecnologı́a FPGA, la cual permitı́a
construir cualquier tipo de circuito digital en base a unos bloques básicos funcionales que ya venı́an
integrados en el dispositivo, lo cual facilitaba trabajar con un nivel de abstracción mucho más alto a
un coste menor que lo que se habı́a venido usando hasta entonces. Los FPGA(Field Programable
Gate Array, Matriz de puertas programables en campo) son unos dispositivos que en su interior
contienen múltiples bloques lógicos y recursos de interconexión configurables. Esto nos
permite utilizar múltiples funciones lógicas preestablecidas y conectarlas como nos con-
venga.

12
Universidad de Alicante: Grado en ingenierı́a informática.

Figura 11: Diagrama estructural general de un FPGA simple. Fuente: https://es.wikipedia.org

Podrı́amos ver los FPGA como una enorme matriz de puertas lógicas, elementos de memoria y
otras funcionalidades que debemos combinar de una manera especı́fica para replicar circuitos especı́fi-
cos. En el fondo, los FPGA modernos son un poco más complejos que lo descrito y contienen más
elementos, pero esta idea básica nos servirá para entenderlos medianamente bien.

Figura 12: Bloques y unidades auxiliares de un FPGA Spartan-7. Fuente:


http://www.iamelectronic.com

Lo más increı́ble, es que no necesitaremos alterar en absoluto la estructura fı́sica del dispositivo,
toda su configuración se realiza programáticamente. Por lo tanto pasamos de tener que desarrollar
hardware en un proceso bastante delicado en el que se trabajaba directamente sobre ((el metal)) (bare
metal) a poder construir circuitos enteros sin tener que tocar ni un solo componente fı́sico ni pieza de
hardware, tan solo necesitamos diseñar la lógica del circuito, implementarla mediante un lenguaje de
descripción y grabarla en el FPGA.

13
5 ESTADO DEL ARTE

Por lo tanto, programar un FPGA consistirá en definir que elementos van conectados
con que elementos, y como se transmitirán las señales entre ellos mediante código, lo cual evi-
dentemente abarata los costes de implementación y reduce el impacto de cualquier posible error de
desarrollo.
Existen varios lenguajes para configurar FPGA’s, pero una de las alternativas con las que más se
trabaja a dı́a de hoy es VHDL, el cual es el que se usará aquı́.
Es muy importante tener claro que, aunque hablemos de programar, no se está haciendo alusión a
la programación al uso que se utiliza en los sistemas informáticos, ya que esa programación consiste en
instruir a un computador sobre que debe hacer, en base a las instrucciones básicas que el programador
le indica. La implementación en FPGA’s tiene más que ver con la construcción del mismo hardware,
estamos definiendo como va a funcionar un sistema y lo que puede hacer, no le estamos indicando lo
que debe hacer. Se prestará más atención a esta sutileza más adelante.

5. Estado del arte

La industria tecnológica no deja de ser otra cosa que una industria, como su nombre indica. Por
lo tanto se mueve por intereses económicos, lo que lleva a estudiar los márgenes de beneficios meticu-
losamente. Lo ideal para las empresas de esta industria es siempre abaratar los costes de producción
para sus productos, y si se pueden obtener las mismas prestaciones para un dispositivo a menor coste,
entonces mejor aún. La industria del microprocesador por ejemplo, basa su modelo de negocio en
la producción en masa principalmente. Emplean un enorme inversión inicial para diseñar una pieza
de tecnologı́a y montar la infraestructura para producirla y luego venden tantas unidades como sea
posible, a un coste de fabricación relativamente bajo.

Es por ello que los FPGA’s están adquiriendo tanta popularidad en esta industria, ya que
permiten diseñar y producir circuitos muy baratos que luego se pueden integrar en sistemas más com-
plejos. A pesar de ser más baratos, sus prestaciones no tienen nada que envidiar a otros dispositivos
basados en microcontrolador o microprocesador. De hecho, para una aplicación especı́fica, los FPGA
son mucho más rápido que estos dos últimos(lógicamente, ya que los antes nombrados son hardware
de cómputo de propósito general).

Pese a todo, un FPGA sigue sin poder competir con un ASIC clásico, pero los costes del prime-
ro son mucho menores. Por lo tanto, para cada situación se hará siempre un balance entre coste y
prestaciones dependiendo de las necesidades de la solución. Por ejemplo, si quisieramos emular un
procesador de 8 bits de una consola antigua, es obvio que la solución más económica y eficaz serı́a
utilizar un FPGA(construir el chip desde cero no serı́a una tarea fácil ni rentable).

Podemos ilustrar esto claramente con el ejemplo de la minerı́a de criptomonedas, un tema muy

14
Universidad de Alicante: Grado en ingenierı́a informática.

actual. Al principio se usaban procesadores de propósito general, para luego pasar a usar GPU’s(las
cuales eran bastante eficientes a la hora de romper estos ((rompecabezas)) criptográficos) debido a su
capacidad de cómputo paralelo. Más tarde se descubrió que emplear FPGA’s configurados especı́fica-
mente para trabajar con algoritmos de cifrado concretos daba unos resultados bastante superiores a
todo lo que se habı́a usado hasta ese entonces. Y a dı́a de hoy, el hardware por excelencia de minerı́a es
ni más ni menos que el ASIC, piezas de tecnologı́a desarrolladas desde el más bajo nivel para cumplir
una sola tarea y cumplirla con resultados espectaculares.

Dicho esto, queda claro que los FPGA son una tecnologı́a que cada vez se está utilizando más,
y que aprender como funcionan y como utilizarlos puede ser un arma muy valiosa en el arsenal de
todo ingeniero.

Figura 13: Dos FPGA de la compañı́a Intel. Fuente: https://newsroom.intel.la

Figura 14: Gráfica de la evolución del hardware de minerı́a de Bitcoin. Puede apreciarse que la di-
ficultad de minerı́a se ha multiplicado considerablemente conforme se han ido implantando nuevas
tecnologı́as de minado. Se observa que a partir de 2012, las plataformas más utilizadas por su eficien-
cia son los ASIC. Fuente: https://www.coindesk.com

15
5 ESTADO DEL ARTE

Figura 15: Estación de minerı́a con hardware ASIC. Fuente: https://www.amazon.es

Figura 16: Granja de minerı́a con múltiples máquinas ASIC. Fuente:


https://www.profesionalreview.com

Figura 17: Comparativa ASIC vs FPGA. Fuente: https://anysilicon.com

16
Sección 2: Desarrollo de circuitos combinacionales básicos con
VHDL.

José Luis Mateo Pastor

1. Introducción y contexto

En esta sección se va a mostrar cómo se implementan circuitos combinacionales mediante el len-


guaje de descripción de hardware VHDL. En este caso, la herramienta que se va a emplear es
el ISE de Xilinx. No se van a explicar los detalles de funcionamiento de este IDE aquı́, nos vamos a
centrar exclusivamente en la descripción de hardware en VHDL, en entender los conceptos sobre los
sistemas digitales y en testear estos sistemas. El código VHDL que se muestre servirá prácticamente
para cualquier otra herramienta, ya que este es un lenguaje bastante estandarizado.

Figura 1: El entorno de desarrollo integrado ISE también posee un módulo de construcción de es-
quemáticos. Estos son los diagramas correspondientes a los circuitos con los que se va a trabajar en
esta sección. Fuente: propia

1
2 PUERTA XOR

2. Puerta XOR
La puerta XOR se llama ası́ porque es una abreviatura de eXclusiveOR. Se trata de una función
lógica que activa la salida sólo cuando las dos entradas son diferentes. Es similar a la puerta
OR, solo que en el caso en el que las dos entradas son 1 la salida es 0.

Figura 2: Código de puerta lógica XOR. Fuente: propia

Aquı́ vemos que el código para esta función es realmente sencillo. Podemos usar directamente el
operador XOR que trae integrado el ISE en sus módulos base. Es importante diferenciar las principales
partes del código.

Lo primero de todo será importar las librerı́as necesarias y especificar que módulos se van a usar.

Tras esto, llegarı́a la sección entity (la entidad), la cual define el componente respecto a sus
entradas y sus salidas, tal y como si de una caja negra se tratase. Las entradas se declararán
con ((in)) y las salidas se declaran con ((out)). En este caso, tanto entradas como salida son del
tipo STD-LOGIC, el cual maneja un valor lógico de un solo bit(true o false, 1 o 0).

Una vez definida la entidad, debemos describir los comportamientos del módulo, esto lo haremos
dentro de su bloque de architecture correspondiente. Como se puede observar en este código,
lo que se hace es enviar directamente una función de las entradas a la salida, en este caso la
función XOR.

Un enfoque bastante conveniente es pensar en la entidad como lo que se ve desde fuera del circuito
y en la arquitectura como lo que hay dentro de este.

2
Universidad de Alicante: Grado en ingenierı́a informática.

Es una buena estrategia realizar uno o varios testbench a nuestros componentes para asegurarnos
de que poseen el comportamiento esperado y el código es correcto.

Figura 3: Simulación de puerta XOR: las dos primeras señales (empezando desde arriba) son las
entradas A y B respectivamente, la tercera señal es la salida obtenida con esas entradas. Fuente:
propia

Si observamos detenidamente, veremos que en esta simulación, la salida sólo muestra un nivel
alto(1) cuando alguna de las dos entradas es 1 y la otra es 0. Es decir, cuando recibimos dos entradas
complementarias. Si la puerta XOR recibe ambas entradas a nivel bajo o ambas a nivel alto, la salida
no se activará.

3. Puertas NAND y NOR


Vamos ahora a ver como se describirı́an las funciones lógicas NAND y NOR:

Figura 4: Códigos para las puertas lógicas NAND y NOR. Fuente: propia

En el caso de estas dos puertas ((compuestas)), se pueden construir en base a las puertas simples
que ya se han presentado en la sección 1. Como se observa en la Figura 1, ambas puertas se forman
colocando un inversor de señal a la salida de una puerta OR o una AND, dependiendo de si se

3
4 MULTIPLEXOR

quiere obtener un puerta NOR o una NAND respectivamente.

A continuación se verificará el funcionamiento de estos componentes mediante unos test:

Figura 5: Simulación puerta NAND. Fuente: propia

Figura 6: Simulación puerta NOR. Fuente: propia

De la misma manera que con la simulación de la puerta XOR, en estas dos simulaciones, las dos
señales superiores son las entradas, y la señal más inferior es la salida.

Si pensamos en que la salida de ambas puertas es sencillamente la negación de las salidas de las
puertas AND y OR corrientes, vemos que efectivamente dichos comportamientos se reflejan en las
simulaciones.

4. Multiplexor

Vamos a implementar un multiplexor de 4 entradas de 8 bits, una entrada de selección de 2 bits y


una salida de 8 bits. Los multiplexores son muy útiles en los circuitos combinacionales, especialmente
cuando se necesita de un sistema de selección de las señales que circularán por un determinado módulo
de un circuito. Por ejemplo, nos será muy conveniente usar uno cuando vayamos a implementar una
ALU,y también son muy útiles en la transmisión de señales, en la siguiente sección lo veremos. En
la sección entity de este componente, vamos a utilizar un tipo de dato STD-LOGIC-VECTOR, el
cual, como su nombre indica, se trata de un tipo de dato compuesto, que puede contener varios bits.
Dado que este multiplexor va a tener entradas y salida de 8 bits, este tipo nos conviene.A la hora de
declarar entradas y salidas con este tipo de dato, debemos especificar cuales serán los bits más y menos
significativos, de manera que implı́citamente se especifica con cuantos bits trabaja esa entrada o salida.

Aquı́ vamos a introducir una nueva sentencia, la sentencia ((with select)). Más adelante se explicará
la utilidad de esta sentencia, pero es importante tener en mente que en VHDL todo funciona
de manera concurrente, ya que en el caso de que estemos acostumbrados a trabajar con lenguajes
de programación, puede costarnos dejar de pensar en ((secuencial)).

4
Universidad de Alicante: Grado en ingenierı́a informática.

Como ya se ha mencionado antes, los multiplexores son módulos lógicos muy útiles, y los usaremos
en breve para construir nuestra ALU, ya que nos permiten manejar varias señales con un sólo
circuito, eligiendo cuál de ellas se va a usar en cada momento, por ejemplo, podremos tener varias
operaciones realizándose sobre las mismas señales, y elegir que resultado queremos dejar pasar. Este
circuito se puede construir fácilmente con puertas AND, OR y negadores.

A continuación se va a mostrar el código VHDL completo para implementar este componente:

Figura 7: Código VHDL para la implementación de un multiplexor 4 a 1. Fuente: propia

Podemos ver que nuestra entidad posee 4 entradas y una salida (por eso se llama multiple-
xor 4 a 1, conmuta 4 entradas) y que con un bus de selección de 2 bits se pueden multiplexar
estas 4 entradas. Para el comportamiento de este módulo se puede emplear una sentencia [with x
select], la cual actua como una especie de condicional (podemos verlo como una sentencia [switch]
en la programación convencional). Obviamente todos los casos de esta sentencia son excluyentes, no
puede cumplirse más de uno.

Aquı́ lo que se está indicando es que en función del valor de la entrada sel(el selector), la salida
conducirá un valor de entrada u otro. Cuando sel sea 00, la salida reflejará el valor de la primera
entrada,cuando sea 01 se pasará la segunda entrada y ası́ para el resto de casos.

5
4 MULTIPLEXOR

Figura 8: Sı́mbolo esquemático del multiplexor. Fuente: https://allaboutfpga.com

Ahora se va a mostrar el código de simulación que se ha empleado para este sistema. En primer
lugar se deben declarar tanto el componente que representa la entidad como las señales de entrada y
salida. Esta parte es similar en todos los tests.

Figura 9: Código de preparación de la simulación del multiplexor implementado. Fuente: propia

Por último se van a definir la secuencia de acciones que se van a llevar a cabo para probar la si-
mulación. En este caso, vamos a probar todos los estados distintos de la señal de selección, que será
la que altere la salida del multiplexor en base a unas entradas fijas. Esto nos servirá para reflejar que
efectivamente, las entradas se multiplexan de manera correcta.

6
Universidad de Alicante: Grado en ingenierı́a informática.

Pero antes de mostrar los resultados de los testbench, es importante hacer un pequeño inciso. Hay
un caso que no se ha contemplado en el diseño de este multiplexor, y que es muy importante tener
en cuenta a la hora de integrar esta unidad en circuitos más complejos. Se trata del concepto de alta
impedancia, un concepto muy importante en electrónica digital, el cual se trata de un ((estado)) en
el que nada fluye a través del bus. Esto es necesario porque en ocasiones necesitaremos que nuestro
sistema sólo arroje resultados cuando se lo indiquemos, y a menos que no ((bloqueemos)) la salida con
este modo de alta impedancia, el cual no dejará pasar ningún bit de información, podemos incurrir
en errores de funcionamiento.

Figura 10: En este diagrama si que se incluye una entrada de activación o enable, también llamada
de validación. Implementaremos un multiplexor acorde a este diagrama en el siguiente capı́tulo, que
será cuando realmente lo necesitaremos. Fuente: https://www.edudevices.com.ar

En esta figura podemos observar que el multiplexor posee una nueva entrada, la cuál se usará pre-
cisamente para activar este estado de alta impedancia o dejar que el multiplexor opere normalmente.
Esta entrada enable es muy fácil de implementar en VHDL, tan solo habrı́a que añadir una condición
que controle que la salida será la seleccionada cuando enable está activado, y que será Z cuando enable
esté a 0. Implementaremos esto en la siguiente sección, que será cuando lo necesitemos, ya que vamos
a integrar un multiplexor en un circuito más complejo.

Volviendo al tema de las prueba, todos los casos de este testbench se van a probar de manera
secuencial, siempre con cierta latencia entre un caso y el siguiente, la cual podremos configurar me-
diante el código del test. Los casos a probar se escribirán dentro del process stim-proc, y entre cada
caso habrá un wait con un valor en nanosegundos, que será la latencia a introducir entre cada caso de
prueba.

De todas maneras, los detalles relacionados con los testbench no son relevantes en este trabajo,
simplemente se presentarán los resultados. Lo único con lo que nos tenemos que quedar aquı́ es con el
formato de los resultados, saber interpretar estos teniendo en cuenta que lo que se simula son los
distintos estados del sistema a lo largo de un perı́odo definido, en el que se realizarán cambios
secuenciales.

7
4 MULTIPLEXOR

Figura 11: Código de la simulación final para el multiplexor. Fuente: propia

Figura 12: Resultado final de la simulación. Las 4 primeras señales son las entradas A,B,C,D respecti-
vamente. Tras ellas viene la entrada de selección y por último se muestra la salida del módulo. Fuente:
propia

8
Universidad de Alicante: Grado en ingenierı́a informática.

Otra forma de implementar el multiplexor podrı́a ser realizando pura lógica combinacional sobre
la salida. Ya hemos visto que, fijándonos en el circuito correspondiente al multiplexor en la Figura
1(introducción de la sección), se aprecia que se puede construir empleando puertas lógicas básicas.

Figura 13: Alternativa a la implementación con sentencias de selección. Fuente: propia

Vemos como realizando una combinación de las entradas y los distintos bits de la señal de selección
y relacionándolos mediante funciones and, or y not podemos obtener también un sistema multiplexor.

5. Semisumador y sumador completo

Ahora vamos a diseñar un sumador completo de 8 bits, el cuál será la base de todas las operacio-
nes aritméticas que necesitemos realizar. Debemos tener en cuenta que una multiplicación se puede
realizar en base a una serie de sumas, de la misma manera que una división se puede realizar en base
a una serie de restas. Para realizar restas podemos usar nuestro sumador con números en un formato
especial, ya que las ALU de la mayorı́a de las computadoras son incapaces de realizar sustracciones
directas, por tal motivo se utiliza la técnica del Complemento a 2, para efectuar una suma y ob-
tener el resultado de una resta. Se verá esto en la próxima sección, pero debemos quedarnos con la
idea de que el único hardware que vamos a necesitar en nuestra ALU será este sumador, el resto de
operaciones se podrán implementar mediante software.

Como se observa en el diagrama que se muestra a continuación, para implementar el sumador


completo de 8 bits se usarán 8 sumadores de 1 bit. En este diagrama en concreto, se usan
solo sumadores completos, pero si nos fijamos bien, el primer sumador tiene su entrada de acarreo
conectada a tierra, por lo que este se podrı́a sustituir por un semisumador, el cual hace lo mismo que
uno completo, solo que no posee lógica de entrada de acarreo.

Parece intuitivo pensar, al ver el diagrama, que serı́a conveniente modularizar el código en
bloques, dado que de otra manera tendremos que repetir muchas instrucciones en nuestra arquitec-
tura. Serı́a conveniente utilizar un módulo de código especı́fico para el sumador completo de 1 bit, y lo
mismo para el semisumador, de tal manera que cada vez que queramos recurrir a sus instrucciones en
el código solo tengamos que ((invocar)) estos módulos. Dicho esto, para esta implementación particular
del sumador de 8 bits vamos a utilizar sumadores y semisumadores de 1 bit, pasamos a mostrar el
código:

9
5 SEMISUMADOR Y SUMADOR COMPLETO

Figura 14: Diagrama lógico de un sumador completo de 8 bits. Fuente:


https://www.researchgate.net

Figura 15: Código semisumador 1 bit. Fuente: propia

Figura 16: Código sumador 1 bit. Fuente: propia

Volviendo al tema de la modularidad del código, ahora vamos a introducir un nuevo concepto de
VHDL, el cual es muy útil para realizar diseños más complejos empleando otros componentes más
pequeños. Se trata de la instanciación y el uso de componentes.

10
Universidad de Alicante: Grado en ingenierı́a informática.

Mediante la sentencia Component podemos traer la funcionalidad de otro código al módulo ac-
tual, en este caso queremos emplear las funcionalidades de los sumadores y semisumadores de 1 bit
en nuestro sumador de 8 bits. Declarar estos componentes nos permitirá instanciarlos, para usar su
código sin tener que replicarlo.

La declaración de estos componentes es, como se observa en el código presentado a continuación,


muy similar a la de sus entidades correspondientes

Figura 17: Definición de la entidad y declaración de los componentes auxiliares. Fuente: propia

Una vez declarados estos componentes, vamos a hacer uso de ellos tantas veces como se
requiera. En este caso, necesitamos usar 7 sumadores y 1 semisumador, por lo que instanciaremos
dichos componentes en base a estos requisitos.

Cada instancia de un mismo componente debe tener un nombre distinto.

Figura 18: Diagrama de un semisumador(izquierda) y un sumador completo(derecha). Fuente:


https://es.wikipedia.org

11
5 SEMISUMADOR Y SUMADOR COMPLETO

Figura 19: Instanciaciones de los componentes y rutado de los flujos de datos. Fuente: propia

12
Universidad de Alicante: Grado en ingenierı́a informática.

Aquı́ también hemos usado otra nueva herramienta, las señales. No se van a explicar en detalle,
basta con saber que en este caso, se han utilizado para conectar componentes internos de la entidad
superior entre sı́.

Por último se van a mostrar los resultados de una baterı́a de tests que se han realizado para asegurar
el correcto funcionamiento del sumador. Se trata de 5 tests que prueban los casos más importantes.

Figura 20: Las 4 señales son, en orden y empezando desde arriba: Sumando1,Sumando2,Acarreo y
Resultado. Se han probado 5 tipos de casos distintos,y se puede concluir que tanto el sistema de suma
como el subsistema de acarreo funcionan. Fuente: propia

Una vez visitados todos estos conceptos, podremos construir sistemas mucho más avanzados.
Estas bases son los pilares de toda la lógica combinacional en VHDL, y coordinando estas herramientas
de manera inteligente podemos crear circuitos con muchı́simas funcionalidades, no es mucho
más complicado que eso. Lo que vendrı́a después es integrar lógica secuencial, para que nuestros cir-
cuitos puedan poseer persistencia y podamos hacerlos más versátiles, permitiendo crear ası́ complejas
máquinas de estados. Todo esto se verá a lo largo de este trabajo.

Figura 21: Esquema de una pseudo-ALU de 1 bit. No llegarı́a a ser una unidad Aritmético Lógi-
ca completa, porque sólo dispone de 3 operaciones: invertir A, invertir B o sumar A y B. Fuente:
https://www.researchgate.net

13
Sección 3: Desarrollo de circuitos combinacionales
avanzados con VHDL, Diseño e implementación de
una ALU.
José Luis Mateo Pastor

1. Introducción

En esta sección se va a construir una ALU completa de 8 bits. Esta ALU realmente será muy
limitada en cuanto a su hardware, pero iremos viendo que es bastante versátil respecto a sus presta-
ciones. Junto a la ALU, vamos también a implementar una pequeña lógica de control necesaria para
decodificar y ejecutar las operaciones que le lleguen.

2. Resta binaria y Complemento a 2

Como ya se explicó en la sección 2, las unidades aritmético-lógicas de los ordenadores


no tienen un circuito restador de por sı́, si no que utilizan un sumador para realizar
las restas, empleando el mecanismo del Complemento a 2. Por lo tanto, vamos a convertir todas las
posibles operaciones aritméticas a sumas. Transformar una resta en una suma con números decimales
es muy sencillo e intuitivo, aunque para usar este mecanismo en binario habrá que emplear un poco
de trabajo extra. Veamos cuales son los mecanismos que vamos a emplear.

La regla que se va a seguir para representar estas operaciones de suma y resta será la de utilizar
el bit más significativo de un número binario para representar el signo de este (positivo o negativo) y
el resto de bits para representar la magnitud del número. Esto presenta un inconveniente, y es que al
dedicar un bit al signo, perdemos capacidad representativa respecto a la magnitud del número, por lo
tanto, si con un número de 8 bits normalmente podrı́amos representar un rango de 0 a 255, utilizando
un bit para el signo podrı́amos representar un rango de -128 a +127. Este será nuestro caso, ya que
la ALU que aquı́ se va a implementar es de 8 bits.

Respecto al bit de signo, lo establecido es que si es 0 entonces representa un número


positivo, si es 1 estarı́a indicando un número negativo. Los bits de magnitud se interpretarán
de una manera u otra dependiendo de si el signo es negativo o positivo. Para poder usar este formato
de representación, todas las operaciones de suma y resta deberán realizar el paso intermedio de la re-

1
2 RESTA BINARIA Y COMPLEMENTO A 2

presentación en Complemento a 2. Es decir, para operar números con signo, debemos transformarlos
todos a esta codificación.

Para transformar un número binario a Complemento a 2, se deben seguir los siguientes pasos:

Primero vamos a invertir todos los bits del número, esto se conoce como el cálculo del Com-
plemento a 1 de un número binario.

Una vez realizado el complemento a 1, sumaremos un 1 al número resultante, y ya tendremos el


Complemento a 2 del número binario original.

Con el número en Complemento a 2, ya podemos realizar operaciones en base binaria con signo,
tanto sumas como restas.

Por lo tanto, el procedimiento para operar dos números con signo en binario consistirá en el
siguiente:

Si simplemente queremos sumar dos números positivos, entonces lo que se hace es una suma
normal de estos. El Complemento a 2 de un número positivo es el mismo número.

Si queremos restar dos números positivos, entonces lo que se harı́a es sumar el minuendo con
el sustraendo en negativo. Es decir, si quisiéramos calcular el resultado de restar 2 a 4 (4-2),
naturalmente podrı́amos obtener este resultado calculando una suma del tipo ((minuendo + (-
sustraendo))), en este caso la suma serı́a (4 + (-2)). Y aquı́ es donde entra el complemento a 2,
ya que para transformar el sustraendo a su equivalente en negativo, usaremos este sistema.

Si lo que queremos es sumar dos números negativos, será tan sencillo como realizar una suma
de ambos números representados en complemento a 2.

Es muy importante señalar que en realidad existe un paso extra al procedimiento explicado, y
es que cuando el bit de signo sea 1, es decir,cuando el resultado sea un número negativo, se debe
transformar este número de nuevo a su complemento a 2, para que la aritmética sea correcta. El
resultado de este nuevo complemento a 2 nos dará la magnitud real del número, pero su signo será el
mismo que indicaba su bit correspondiente en un primer momento, previo al paso a complemento a 2.

Figura 1: Este ejemplo ilustra perfectamente el procedimiento de resta binaria. Aquı́ vemos que lo
que se está haciendo es transformar la resta en una suma, habiendo representado previamente el
sustraendo mediante el Complemento a 2. Como en este caso el resultado es positivo (el bit de signo
es 0), se deja tal cual. Si el resultado hubiera sido negativo, entonces deberı́a volver a hacerse un
complemento a 2 del resultado, lo cuál nos darı́a la magnitud real de dicho número negativo. Fuente:
http://circuitoslistos.blogspot.com

2
Universidad de Alicante: Grado en ingenierı́a informática.

Figura 2: En este diagrama se observa cómo se puede transformar un sumador de 4 bits en un suma-
dor/restador mediante el uso de puertas XOR y una entrada adicional selectora de la operación. Esto
se puede extrapolar para 8 bits. Fuente: http://www1.frm.utn.edu.ar/arquitectura/unidad2.pdf

Como vemos en la figura anterior, utilizando puertas XOR en combinación con la señal S̄/R
podemos invertir todos los bits del segundo operando. Dado que la señal S̄/R es 1 cuando queremos
restar, todo lo que entre por la puerta XOR, al tener un 1 en una de sus entradas, saldrá invertido.
Esto serı́a el equivalente a realizar el Complemento a 1 sobre el operando b.

3. Implementación del sumador/restador de 8 bits en VHDL


A continuación se mostrará el código VHDL para este componente y se explicará brevemente:

Figura 3: Declaración de la entidad y de los componentes a usar. Fuente: propia

3
3 IMPLEMENTACIÓN DEL SUMADOR/RESTADOR DE 8 BITS EN VHDL

En esta primera parte del código, ya se puede intuir que vamos a reutilizar la mayor parte
de la lógica del sumador que hicimos en la sección anterior, el código es igual en su mayorı́a. Sin
embargo, vamos a añadir una entrada S̄/R a la que hemos llamado ADD-SUB, la cual hará de bit selector
de operación. Otra diferencia es que en este sistema, todos los sumadores van a ser completos, a di-
ferencia del anterior, en el que usábamos 1 semisumador y 7 sumadores completos. Aquı́ también
se ha hecho una pequeña modificación a uno de los sumadores, en concreto al sumador del bit más
significativo, al cuál se le ha añadido una entrada Add-Sub, la cuál podrá leer el bit de selección del
módulo completo, para realizar un alteración de la salida del acarreo en base a esta señal.

Recordemos que con el sistema de puertas XOR conectadas a la señal S̄/R y a los bits del segundo
operando, lo que hacı́amos era un complemento a 1, pero si repasamos la teorı́a antes presentada,
para operar con restas binarias, habı́amos establecido que ibamos a usar el Complemento a 2. Nos
faltarı́a pues, sumar un 1 al resultado de este Complemento a 1, y es por esto que el primer sumador
del sistema es también un sumador completo, en vez de un semisumador, para poder sumar un 1 a
través de la entrada de Carry In, la cual estará a 0 o a 1 dependiendo de si se va a sumar o a restar,
determinado también por la señal S̄/R, por lo que también usaremos el truco de la puerta XOR con
esta entrada Carry In.

A propósito de clarificar esta explicación, se ha preparado un diagrama que sintetiza todo los
descrito:

Figura 4: Se han situado todas la entradas, salidas y señales que se van a utilizar en la implementación
VHDL, con los mismos nombres, para que quede todo más claro. Fuente: propia

4
Universidad de Alicante: Grado en ingenierı́a informática.

Figura 5: Código del sumador completo de 1 bit modificado.Cómo se observa en la imagen, hemos
añadido, como ya hemos dicho, una entrada adicional para controlar la selección de la operación.
Fuente: propia

Figura 6: Código para la interconexión de los sumadores. Fuente: propia

5
4 MULTIPLEXORES CON ENTRADA ENABLE

Figura 7: Resultados de las distintas simulaciones que se han realizado para el sumador en Comple-
mento a 2. Fuente: propia

Nótese que aquı́ los resultados de las simulaciones se han presentado en formato hexadecimal, y a
partir de ahora se usará este formato siempre que sea conveniente. En electrónica digital la mayorı́a
de veces por no decir todas las veces, siempre se representa la información en código hexadecimal. No
se va a explicar aquı́ como funciona el código hexadecimal, eso es algo muy básico y se presupone que
el lector ya debe tener esos conocimientos mı́nimos. Pero es obvio que usar el código hexadecimal nos
va a permitir resumir las ristras de bits y ver los resultados de manera más clara.

Es muy importante tener en cuenta que este circuito aritmético siempre transforma
el segundo operando a complemento a 2 cuando se usa para restar, por lo que este siempre
deberá ser positivo. El primer operador podrá ser negativo, pero para interpretar su magnitud real
deberemos pasarlo a Complemento a 2.

4. Multiplexores con entrada Enable

Hay un aspecto del diseño digital avanzado que es muy importante, y se trata del datapath, es
decir, como fluyen los bits a través del sistema. Cuando construimos circuitos más complejos, lo normal
es que varios componentes compartan una misma lı́nea de transmisión, y estos buses compartidos,
si no se tratan de la manera correcta, pueden ser problemáticos. Debemos cuidar mucho
este aspecto del diseño, y es que aunque los componentes individuales estén bien diseñados, si su
interconexión no se realiza de la manera correcta, el sistema no será de ningún modo correcto.
Y aquı́ es donde entra en acción el concepto de alta impedancia. Imaginemos un caso en el que
tenemos un bus conectado a un registro en el que vertemos los datos que queremos que se guarden en
dicho registro. Ahora imaginemos que ese bus está compartido por varios módulos, por ejemplo por
la ALU y por el circuito de acceso a memoria, ya que dependiendo de la instrucción que el procesador
ejecute, vamos a querer que la información a colocar en el registro provenga de memoria o del resultado
de una operación realizada en la ALU. Sin tener en cuenta el tercer estado de alta impedancia, al tener
una conexión directa entre estos dos componentes, podrı́an ocurrir muchos efectos indeseados, desde
que los datos que circularan por el bus no fueran los correctos hasta que el sistema cortocircuitara.

6
Universidad de Alicante: Grado en ingenierı́a informática.

Es por eso que necesitamos usar estados de alta impedancia, para asegurarnos de que un bus
no está transportando información de una fuente indeseada, y que sólo comunica desde
una fuente cada vez. En este ejemplo concreto, lo que se harı́a es, que cuando lo que quisiéramos
poner en el registro fuera un resultado de la ALU, la salida del circuito de memoria se pondrı́a en alta
impedancia, lo cual provocarı́a que esta no enviara ninguna señal al bus, y en el caso de que lo que
quisiéramos fuera enviar un dato de memoria a este registro compartido, lo que pondrı́amos en alta
impedancia serı́a la salida de la ALU.

Figura 8: Diagrama del circuito y tabla de verdad para el búffer triestado. Podemos ver que cuando la
señal de enable es 0 la salida del circuito es Z(alta impedancia), lo que equivale a un circuito abierto,
es decir, no pasa ninguna señal a través del bus. Fuente: https://semiconductorclub.com

Figura 9: Como se observa en la imagen, conectando la salida del multiplexor al buffer y usando la
señal Enable para controlarlo, podemos crear un multiplexor con entrada de activación que tendrá su
salida a Z cuando la entrada Enable sea 0. Fuente: propia

Figura 10: Y aquı́ ya tendrı́amos el diagrama de alto nivel de este sistema, todo encapsulado
dentro de un único componente. Fuente: https://personales.unican.es/manzanom/planantiguo/
edigitali/muxg7_09.pdf

7
4 MULTIPLEXORES CON ENTRADA ENABLE

Figura 11: Podemos ver que se han usado nuevas sentencias VHDL que no se habı́an presentado antes.
Vamos a explicarlas en un momento. Fuente: propia

Antes de comentar el código, se mostrarán los resultados.

Figura 12: Aquı́ comprobamos que independientemente de lo que haya en el resto de entradas, si
Enable está a 0, la salida será Z. Para Enable=1, el sistema funciona como un multiplexor cualquiera.
Fuente: propia

8
Universidad de Alicante: Grado en ingenierı́a informática.

4.1. La sentencia Process

Hemos observado que para la implementación de este multiplexor hemos hecho las cosas un poco
diferente a lo que habı́amos hecho hasta ahora. Es un buen momento para hablar de la sentencia
Process. Se trata de una estructura que define los lı́mites de un dominio que se ejecutará si y sólo si
alguna de las señales de su lista de sensibilidad se ha modificado en un paso anterior. Esto se traduce
en que, todo lo que esté dentro del bloque process se ejecutará cada vez que una de las señales sensibles
cambie de valor. Por ello es crucial definir con cuidado esta lista de sensibilidad, la mayor parte de
errores a la hora de usar Process proviene de ahı́. También hay que tener en cuenta que esta sentencia
tiene ”memoria”, lo cual quiere decir que si una señal no se cambia en un instante t+1 en el que se ha
disparado la sentencia, mantendrá el valor del instante t previo, es decir, de la última vez que se ejecutó.

Dentro de este bloque,las sentencias se ejecutarán de manera secuencial, también deberemos tener
esto en cuenta, el orden de la asignación secuencial es muy importante. En este código, hemos utili-
zado un nuevo tipo de sentencia condicional también, más parecida a los lenguajes de programación
al uso. Se trata de la sentencia if-else, la cual debe siempre ir dentro de un bloque Process. Seguro
que al lector le resultará muy familiar esta estructura, pero por si acaso, se va a explicar someramente.

La sentencia if definirá un bloque de código que se ejecutará solo si se cumple la condición asocia-
da,(lo que va entre paréntesis antes del ((then))). Podemos combinar esta sentencia con otros dos tipos
más, las sentencias ((else)) y ((elsif)), las cuales son similares, pero con un matiz muy importante. En
el caso de la sentencia ((else)), su bloque de código asociado se ejecutará siempre que no se cumpla el
bloque asociado a la sentencia ((if)) o ((elsif)) previo. Con la sentencia ((elsif)) pasa lo mismo, sólo que
además de considerarse cuando el flujo del programa no entra en el bloque condicional previo, sólo
se ejecutará si se cumple su condición asociada. Esto serı́a muy parecido a tener dos sentencia ((if))
continuas, sólo que con ((elsif)) ambas sentencias son mutuamente excluyentes, o se cumple una de ellas
o no se cumple ninguna, pero nunca ambas.

5. Implementación de la ALU y su lógica de control


Para finalizar esta sección, se va a implementar la Unidad Aritmético Lógica de la que tanto se ha
hablado, empleando todo el conocimiento que se ha expuesto a lo largo de toda la sección. Para esta
ALU, usaremos la siguiente ((arquitectura)):

OpCode Instrucción Efecto


0000 ADD A,B Suma el contenido de los registros A y B y coloca el resultado en A
0001 SUB A,B Resta el contenido del registro B al de A y coloca el resultado en A
0010 XOR A,B Realiza una operación XOR con A y B y coloca el resultado en A
0011 NOT A Invierte el contenido del registro A y coloca el resultado en este.

9
5 IMPLEMENTACIÓN DE LA ALU Y SU LÓGICA DE CONTROL

Podemos observar que esta ALU tan sólo puede realizar 4 operaciones: suma,resta,xor e
inversión. Por ello, con tan sólo 2 bits podemos seleccionar las operaciones que se desea usar, estos
dos bits los nombraremos como bit de control 0 (el de peso 0, el menos significativo) y bit de control
1 (en la posición 1). Sin embargo, observamos que el opcode tiene 4 bits, y esto se debe a que vamos
a utilizar instrucciones de 8 bits, en las que los 4 bits mas significativos codifican la operación y los 4
bits menos significativos codifican el operando (cuando se use direccionamiento inmediato). De todas
formas estos detalles ahora mismo no son relevante, solo nos conviene saber que en este caso, el código
de operación sólo muestra los 4 bits más relevantes.

En base a este esquema, podemos deducir fácilmente que nuestra entidad VHDL tendrá 3
entradas (A,B y Control) y 4 salidas (Resultado,Bit de Carry,Bit de Overflow y Bit de comparación
con cero). Podrı́amos también configurar nuestra entidad con 4 entradas (separando la entrada de
control en 2 subentradas como se hace en el diagrama), pero por propósitos de sencillez, se hará con
una sola entrada de control.

Figura 13: Diseño esquemático de la ALU que vamos a implementar. Además de implementar las
operaciones comentadas, también tendrá bits detectores de Desbordamiento y Resultado Cero, y un
bit de Acarreo. Fuente: propia

Lógicamente, todos los componentes de esta ALU son conocidos, y de hecho hemos visto como se
implementaban cada uno de ellos. Ahora podrı́amos optar por reutilizar el código que hemos escrito
para todos estos componentes e interconectarlos mediante instanciaciones. Pero esto no es estricta-
mente necesario, de hecho podrı́amos sintetizar toda la lógica subyacente a este circuito muchı́simo
más, empleando las distintas herramientas que nos brinda VHDL, ya que es precisamente esta poten-
cia de sı́ntesis lo que caracteriza a VHDL, podemos describir el sistema de una manera más
funcional, en vez de tener que ceñirnos a su estructura de componentes y sus conexiones.

10
Universidad de Alicante: Grado en ingenierı́a informática.

Figura 14: Prueba del sumador de la ALU. Fuente: propia

Figura 15: Prueba del restador de la ALU. Fuente: propia

Figura 16: Prueba de XOR,NOT y Alta impedancia de la ALU. Fuente: propia

Figura 17: Declaración de la entidad, con sus entradas y salidas y señales auxiliares. Fuente: propia

Y ahora se va a mostrar el código utilizado para la implementación:

11
5 IMPLEMENTACIÓN DE LA ALU Y SU LÓGICA DE CONTROL

Figura 18: Código de la función de suma de la ALU. Fuente: propia

Figura 19: Código de la función de resta de la ALU. Fuente: propia

12
Universidad de Alicante: Grado en ingenierı́a informática.

Figura 20: Código de las funciones XOR y NOT de la ALU. Fuente: propia

Para implementar esta ALU se ha utilizado un Process, además de una nueva sentencia condicional
Case. Las sentencias Case son sentencias condicionales que ejecutarán un código u otro
dependiendo del valor de un elemento de control, similar a las sentencias With/Select (aunque
estas últimas son de asignación condicional). En este caso particular, usaremos la entrada Control
como elemento de selección, dependiendo del valor de esta entrada, se ejecutará un bloque de código
u otro.

13
5 IMPLEMENTACIÓN DE LA ALU Y SU LÓGICA DE CONTROL

Las señales que se han declarado se usarán para hacer operaciones de conversión y comparación
respecto a los resultados. Por ejemplo, las señales opResult,opResultSum y opResultRes8 se usarán
para la comparación con 0, y la señal opResultRes9 se usará para manejar un resultado de 9 bits sobre
las operaciones con 8 bits. Esto en un principio se pensó para detectar Carry y Overflow en la resta,
aunque al final se descartó este mecanismo. Pero aún ası́ se han dejado estas señales de 9 bits, porque
podrı́an ser útiles en futuras modificaciones. De hecho, aunque no se detecta el Overflow y el Carry
en la resta, la operación se hace con 9 bits igualmente.

Para este sistema, la entrada Control será una entrada de 4 bits que va a representar los bits de
los OpCode que controlarán que operaciones se van a realizar con la ALU. Si nos fijamos en la tabla
con los códigos de operación que se ha mostrado antes, todas las operaciones de la ALU tienen sus 2
bits más significativos a 0, por lo que cuando alguno de estos dos bits esté a 1, deberemos colocar la
salida de la ALU en alta impedancia, ya que la instrucción no va a usar la unidad Aritmético Lógica.
Esto sobre el código se puede implementar con un caso ((Default)) que se indica con un ((when others))
en esta estructura Case.

Por lo tanto, cuando la entrada de Control no sea ni 0000 ni 0001 ni 0010 ni 0011, se ejecutará
este caso por defecto, que colocará todos los bits de salida a Z. En este código se observa que la forma
de colocar un mismo valor para todos los bits, en vez de definir explı́citamente cada uno de ellos, es
con la instrucción ((others)), que nos servirá para establecer un mismo valor para todos los bits con
una asignación que equivaldrı́a a dar valor a un solo bit.

Respecto al código de las distintas funciones de la ALU, las funciones de XOR y NOT son muy
sencillas de implementar, ya que vienen por defecto en la librerı́a estándar de VHDL. Por lo tanto,
en estos casos tan sólo tendremos que enviar a la salida el resultado directo de la operación lógica
respectiva, pero también deberemos asignar valor a los bits de Carry y Overflow, que en estos casos
será 0, y para el bit de Zero, deberemos comprobar que el resultado sea o no igual a cero, y en base
a esto asignar el valor pertinente a este bit. Para detectar el desbordamiento en la suma se ha
usado el bit de signo. Si el resultado de la suma tiene un bit de signo negativo, entonces
claramente ha habido un desbordamiento debido al acarreo, por lo que siempre que se presente
un acarreo sobre los últimos bits de magnitud, habrá un desbordamiento.

Por último, comentar los resultados de los testbench realizados. Lo más notable a resaltar es
que efectivamente con estos test se comprueba que cuando la ALU no se está usando, su salida se
establece a alta impedancia, lo que permite usar esta ALU en buses compartidos. Otro resultado
importante es que cuando el resultado de una operación XOR es igual a cero, si lo pensamos bien,
lo que se confirma es que ambos operandos son el mismo número, esto se respresenta claramente
en uno de los tests. Por lo tanto, mediante la combinación de este Zero bit y la operación XOR
nuestra ALU tiene la capacidad de comparar dos números.

14
Universidad de Alicante: Grado en ingenierı́a informática.

6. Reimplementación de la ALU

Lo que hemos hecho con la implementación anterior de la ALU es válido, pero no es la manera más
práctica de hacer las cosas en VHDL. Hemos estado construyendo la ALU de una manera totalmente
clásica, y esto se ha hecho con fines didácticos, para presentar toda la teorı́a que subyace a este tema.
Pero hay que tener en cuenta que al usar VHDL podemos abstraernos muchı́simo más de
todos estos detalles, lo cual facilita mucho el diseño de esta clase de sistemas.

Vamos a ver cómo se implementarı́a una ALU de la manera ”moderna”, es decir, más acorde a
las prestaciones de VHDL, ya que se hace mucho más sencillo que todo lo que hemos explicado, por
ejemplo, no tendremos que pensar en Complementos a 2 ni en lógica de control de por sı́, pero viene
bien saber esas cosas y por ello se han explicado.

Figura 21: Código de la ALU mejorado. Fuente: propia

El código que se observa en esta figura describe una ALU mucho más completa que la anterior,
puede realizar un surtido mayor de operaciones. Vemos que a pesar de tener pocas lineas más
que el código de la ALU que hemos descrito en el apartado anterior, este sistema es más po-

15
6 REIMPLEMENTACIÓN DE LA ALU

tente. No sólo es capaz de realizar sumas y restas con signo, si no también multiplicaciones y todas
las funciones lógicas básicas. Cómo se habı́a comentado antes, aquı́ la abstracción es mucho mayor
que en la implementación anterior, vemos que nosotros simplemente le indicamos al sistema lo que
tiene que hacer dependiendo del código de operación, y no nos tenemos que preocupar por cómo lo hace.

Por ejemplo, la multiplicación en sistemas clásicos se implementa mediante circuiterı́a compleja,


hay que usar determinados algoritmos (como el algoritmo de Booth) que en base a distintas sumas
y desplazamientos lógicos realizan esta operación. Si quisiéramos construir este sistema de la manera
que hemos presentado hasta ahora tendrı́amos que conectar múltiples sumadores y registros de des-
plazamiento entre sı́ de determinada manera para conseguir la capacidad de multiplicar. Pero en una
descripción moderna de un multiplicador en VHDL no debemos pensar en nada de eso, simplemen-
te describimos la operación y la FPGA sobre la que grabemos el sistema se encargará del resto, la
mayorı́a de FPGA actuales ya traen circuitos multiplicadores muy completos. De igual
modo, la resta tan sólo se deberá indicar, sin pensar en Complemento a 2 ni nada de eso, tan sólo
indicaremos que queremos una resta con signo y nos despreocuparemos de lo demás.

Lo único que puede ser necesario explicar en este código es la conversión que se ha realizado en las
operaciones para que puedan trabajar bien con la multiplicación. Al describir una multiplicación de
dos operandos de 8 bits, el ISE esperará un resultado de 16 bits por defecto, y si intentamos mandar
el resultado de esta multiplicación a una salida de 8 bits, nos saltará un error debido a esto. Sin
embargo, la suma de dos números de 8 bits da un resultado de 8 bits, y también el resto de opera-
ciones lógicas darán resultados de 8 bits.Es por ello que debemos ampliar esos resultados a 16 bits,
ya que si optáramos por la opción de recortar el resultado de la multiplicación a 8 bits, estarı́amos
perdiendo información de esta, sin embargo, si concatenamos bits de relleno a las operaciones de 8
bits, no estamos alterando realmente el resultado, y eso si nos permitirá usar una salida de 16 bits
para todas las operaciones del sistema.

Figura 22: Simulación de las operaciones de la ALU. Fuente: propia

Figura 23: Otra simulación en la que se aprecia el funcionamiento de los bits de Cero, Acarreo y
Desbordamiento de la ALU. Fuente: propia

16
Universidad de Alicante: Grado en ingenierı́a informática.

Por último se ha realizado una simulación de todas las operaciones que puede realizar la ALU, y
como se observa en los resultados, esta funciona correctamente. Es cierto que en este caso no se han
usado estados de alta impedancia, ya que este estado no necesariamente debe usarse dentro del propio
módulo de la ALU.

Figura 24: Circuitos multiplicadores integrados en una FPGA Spartan 3. Fuente: https://
programmerclick.com/article/56121128084/

Figura 25: Tarjeta de desarrollo Spartan 3E. Fuente: https://www.researchgate.net/figure/


Figura-2-Tarjeta-de-desarrollo-Spartan-3E-FPGA-Starter-Kit-Board_fig1_299436893

Todo lo que se ha descrito aquı́ se podrı́a implementar sin problemas sobre un sistema cómo el que
se muestra en la figura de arriba. En el caso de la figura 25, se trata de una tarjeta de desarrollo, la cual
posee una FPGA y múltiples periféricos y otras utilidades más para realizar prototipados de sistemas
de manera sencilla. Vemos que esta tarjeta Spartan es de Xilinx, por lo que tendrı́amos integración
total con el entorno de ISE. Sobre la práctica, configurar una ALU cómo las que se han mostrado en
este capı́tulo en una de estas tarjetas no serı́a muy diferente a lo que hemos visto, tan sólo restarı́a el
paso adicional de grabar la configuración del sistema que hemos descrito en la propia FPGA.

17
Sección 4: Desarrollo de circuitos secuenciales básicos
con VHDL.
José Luis Mateo Pastor

1. Introducción

Hasta ahora, hemos trabajado sólo con circuitos combinacionales. Vamos a dar por cerrada esa
etapa y comenzar a explorar el desarrollo de sistemas secuenciales en VHDL. Recordemos que en los
circuitos secuenciales, las salidas ya no solo dependen de las entradas en ese momento, si
no del estado en el que se encuentre el sistema, determinado también por las entradas
pasadas, y debido a esta necesidad de recordar estas entradas pasadas, los circuitos secuenciales ne-
cesitan elementos de memoria.

Debido a su capacidad para almacenar información para ser utilizada en momentos posteriores, los
circuitos secuenciales son útiles en muchas aplicaciones, incluyendo las máquinas de estados, elementos
importantı́simos en los sistemas digitales avanzados. Los circuitos secuenciales también usan un reloj
interno, por lo que son útiles en cualquier sistema que se base en una sincronización exacta.
Las unidades básicas de los elementos de memoria, los biestables, están hechos de las mismas puer-
tas lógicas que los circuitos combinacionales. Lo que marca la diferencia aquı́ es cómo se interconectan
esas puertas. Para que un circuito ((recuerde)) su valor actual, debemos conectar la salida de este directa
o indirectamente a su propia entrada, lo que se conoce cómo
((realimentación)). Esto es la base de todos los elementos de memoria, empezando por los biestables,
que son la unidad mı́nima de memoria que se puede manejar en electrónica digital.

2. Flip-Flops

Los Flip-Flops o biestables son los elementos básicos de memoria para almacenar información. Es-
tos elementos son capaces de almacenar 1 bit de datos, por lo tanto sólo pueden estar en dos estados
distintos: 0 o 1. Existen varios tipos de Flip-Flops, los cuatro principales son los SR,D,JK y T. Todos
son válidos y se puede construir cualquier circuito con cualquiera de ellos, pero hoy en dı́a los cir-
cuitos secuenciales se construyen principalmente con los de tipo D.

1
2 FLIP-FLOPS

Dicho esto, vamos a implementar un Flip-Flop D en VHDL. Pero antes vamos a analizarlo un poco
más en profundidad.

Figura 1: Sı́mbolo de un Flip-Flop D. Fuente: https://www.build-electronic-circuits.com/


4000-series-integrated-circuits/ic-4013

Como se observa en la figura 1, este componente posee 2 entradas y 2 salidas, donde la segunda
salida es sencillamente el negado de la primera. De hecho, la segunda salida no es imprescindible,
por lo que en muchos casos no se usa e incluso hay biestables que ni siquiera tienen esta salida. La
forma en la que trabaja este biestable es la siguiente: cada vez que se registre un flanco de reloj
(puede ser de subida o de bajada), el cuál consiste en el paso de la señal de reloj de un estado a otro
(0 a 1 en el caso de subida y 1 a 0 en el caso de bajada), lo que haya en la entrada de datos (Data
in) se registrará, es decir, se pasará a la salida. Y este estado se mantendrá hasta que se vuelva a
registrar un cambio de esta entrada en un flanco de reloj. Es decir, mientras no se desee cam-
biar expresamente el estado de este biestable, mantendrá el último estado que se registró.

Aquı́ es importante tener en cuenta que la alteración del estado que se va a almacenar siempre se
realizará de manera sı́ncrona, es decir, el evento principal que dirigirá este cambio será el flanco de
reloj.

Figura 2: Sı́mbolo de un Flip-Flop D con entradas Set y Clear. Fuente: https://www.


build-electronic-circuits.com/4000-series-integrated-circuits/ic-4013

También existe una versión más completa de este componente, con 2 entradas adicionales, como
se observa en la figura 2. Estas entradas se usan para modificar el estado del biestable de manera
ası́ncrona, es decir, independientemente de la señal de reloj, aunque también se pueden usar de ma-
nera sı́ncrona.

2
Universidad de Alicante: Grado en ingenierı́a informática.

No es que uno sea mejor que otro propiamente dicho, simplemente usaremos un modelo con
o sin entradas SET/CLR dependiendo de las necesidades y requerimientos que haya que
cubrir en el diseño del sistema. Por ello, vamos a implementar ambas versiones en VHDL, ya que la
diferencia en el código de una a otra va a ser mı́nima.

Figura 3: Código de Flip-Flop simple (izquierda) y versión con entradas Set-Clear (derecha). Fuente:
propia

Vamos a explicar primero la parte común a ambas versiones. Sólo se usará una salida, la salida
Q negada no se va a implementar. Vamos a usar un process, que es idóneo para construir sistemas
secuenciales. En ambos casos, para la parte principal del biestable, simplemente usaremos un con-
dicional que cuando detecte un flanco de subida enviará el bit que coloquemos en la
entrada D a la salida Q, lo cuál a su vez establecerá en nuevo estado del biestable, es decir, lo que
coloquemos en D será lo que este biestable memorice.

Lo que cambiarı́a en el código de la versión con entradas Set y Clear (ambas ası́ncronas en este
caso) son principalmente dos elementos:

En la lista sensible del process deberemos añadir las dos entradas Set y Clear, ya que ahora
no sólo el reloj desencadenará eventos, y al ser una versión ası́ncrona, ambas entradas son in-

3
3 REGISTROS

dependientes de este. Si quisiéramos usar estas entradas de forma sı́ncrona, entonces la lista de
sensibilidad se quedarı́a como estaba, con sólo la señal de reloj en ella.

Dentro del código del process también deberemos hacer modificaciones. En este caso, se tratará
sencillamente de añadir dos nuevos condicionales que gestionen la lógica de Set y Clear.

Figura 4: Simulación del Flip-Flop simple, sin entradas Set-Clear. Fuente: propia

Lo más importante a resaltar en esta simulación son los aparentes retardos que se observan al
cambiar el estado del biestable. Podemos ver que no conciden de manera exacta con los cambios de D,
y esto naturalmente se debe a que la propagación de D a Q no es instantánea, si no que se produce
en el momento en el que se detecta un flanco de subida y a su vez hay información en D para
transmitir (de ahı́ el nombre de biestable D, de Delay o retardo).

Figura 5: Simulación del Flip-Flop con entradas Set-Clear. Fuente: propia

En esta simulación lo único diferente a remarcar es el uso de las señales Set y Clear, y observar
cómo en este caso, estas señales actúan de manera instantánea, no hay retardo en la propagación, ya
que funcionan de manera ası́ncrona y por ende no deben esperar a un flanco de reloj.

3. Registros

Una vez ya tenemos elementos básicos de memoria, ya podemos empezar a almacenar la informa-
ción. Pero hemos visto que los biestables sólo pueden almacenar 1 bit de información, por lo que quizá
deberı́amos pensar en alguna solución más escalable. Y ahı́ es donde entran los registros, los
cuáles no son otra cosa que una agrupación de biestables conectados. Es decir, si quisiéra-
mos construir un registro de 8 bits, lo que se harı́a es conectar 8 biestables, el cómo se conecten
dependerá del tipo de registro, ya que hay varios.

Pero en esta sección de va a trabajar sólo con dos tipos: serie-serie o SISO y paralelo-paralelo
o PIPO. Cuando decimos que un registro es serie-serie a lo que nos estamos refiriendo es a que los
datos entran y salen de este en serie, ı́dem para el caso paralelo-paralelo.

4
Universidad de Alicante: Grado en ingenierı́a informática.

Figura 6: Registro serie-serie (arriba) y paralelo-paralelo (abajo). Fuente: http://cienciasfera.


com/materiales/tecnologia/tecno02/tema12/21_registros_de_desplazamiento.html

Cómo podemos observar en las figuras, en el caso del registro PIPO, no hay ninguna di-
ferencia notable respecto a un biestable individual, excepto que en el caso del registro la
capacidad de almacenamiento es mayor. Es decir, un registro PIPO de 8 bits se podrı́a considerar
cómo una especie de biestable ((extendido)) que en vez de almacenar tan sólo 1 bit puede almacenar
8. Una vez más, debemos tener en cuenta que podrı́amos seguir un acercamiento más estructural
en la implementación (utilizar componentes e instanciaciones), pero estos esquemas tan sólo se han
presentado con la intención de clarificar el funcionamiento de los registros, y precisamente lo bueno
de VHDL es esta versatilidad que nos ofrece, ya que no debemos seguir las reglas convencionales a la
hora de la implementación.

El caso del registro SISO es un poco más particular. Y es que este tipo de registro también
se conoce cómo registro de desplazamiento, el cuál es muy usado en algunos sistemas digitales
avanzados. Se denomina registro de desplazamiento porque al tener que introducir los datos en se-
rie, y no siendo este registro otra cosa que un conjunto de biestables conectados en cascada, cada bit
de información se propagará de un biestable a otro, ((desplazando)) la información que hubiera en estos.

Aunque en la figura anterior se sintetiza bastante bien la idea detrás de los registros serie y
paralelo, los diagramas que se muestran carecen de algunas entradas que son conceptualmente muy
importantes a la hora de integrar los registros en sistemas más complejos. Se trata de las entradas
Load y OutputEnable. La entrada Load será cómo una especie de ((enable)) de la entrada del registro,
lo que haya en la Entrada sólo se cargará en el registro si la señal de Load está activa. La entrada de
OutputEnable nos permitirá extraer el contenido de los registros de manera ası́ncrona, sólo cuando
esta señal esté activa, se colocará en la Salida el contenido de estos.

5
3 REGISTROS

Figura 7: Código de un registro SISO (izq.) y PIPO (der.). Fuente: propia

Se observa en la figura anterior la implementación VHDL de las dos versiones de registros, ambas
con señal de activación Load y con Reset sı́ncrono (a diferencia del reset ası́ncrono implementado
previemente en la parte de los Flip-Flops). La señal de Load en este caso cumple una función similar
al uso que hacı́amos de la entrada en alta impedancia en la simulación de los biestables, permitir que
ninguna información se propague de la entrada al circuito de memoria cuando esté a 0. En este caso,
sólo cuando Load esté activa, se registrará lo que haya en la entrada. La entrada SR en este
caso cumple el papel de señal de activación de la salida (Output Enable), cuando esta entrada reciba
un 1, el registro enviará su contenido a la salida, independientemente del reloj.

En el caso del registro PIPO, no hay que realizar ningún desplazamiento, por lo que el código no
tiene nada especial, de hecho, si nos fijamos, es exactamente el mismo código que para el registro serie,
sólo que en este caso la entrada y la salida son vectores, ya que ahora lo que se almacena son varios bits.

Ahora vamos a ver el código de la versión serie-serie. La parte más importante de este es el despla-
zamiento de los bits. Con el operador & vamos a concatenar los bits de la entrada con la información
de los biestables internos. Si observamos con atención, estamos combinando el bit de Input con los 7
bits más significativos de la señal aux, la cual se usa como una señal auxiliar para gestionar el estado
interno del registro. Puede que cueste intuirlo, pero dado que cada vez que se realiza la concatenación,
aux pierde un bit, ası́ es cómo se está consiguiendo el efecto de desplazamiento, que en este caso serı́a

6
Universidad de Alicante: Grado en ingenierı́a informática.

un desplazamiento a la derecha, ya que al colocar en el bit más significativo a Input, el bit que se
reemplazará al realizar esta concatenación será el menos significativo, el que está más a la derecha.

Figura 8: Simulación de registro serie-serie. Fuente: propia

Lo primero a destacar, es que en estas simulaciones se han desplegado todos los componentes del
vector de salida en el visualizador, esto con el propósito de poder observar cómo cambian cada uno de
los bits del vector de memoria interno (que en un sistema con un enfoque ((clásico)) corresponderı́a a
cada biestable que conforma el registro). En este caso, dado que el registro es de entrada serie y salida
serie, el registro funciona con desplazamiento, y esto se aprecia bastante bien en la simulación, ya que
se puede ver cómo la entrada se propaga de un bit del vector al siguiente con cada pulso de reloj.

Figura 9: Simulación de registro paralelo-paralelo. Fuente: propia

En el caso de este registro PIPO, también vamos a analizar cada una de los bits que componen el
estado interno. Vemos que al ser todo en paralelo, todos los bits cambian de estado simultáneamente,
estableciendo cada uno de ellos al valor que aparezca en el bit que le corresponde de la entrada. Al ser
la entrada paralela, esta vez se trabajará con vectores, ya no se inyectará un bit distinto de información
en cada pulso de reloj, cada vez que se active el registro, se modificarán los 8 bits a la vez.

Vemos que en ambos casos, tanto en el registro serie como en el paralelo, se puede observar en
las simulaciones que cuando la señal SR está a 0, la salida es el estado de alta impedancia. También
podemos ver que cuando Load no está a 1, no se altera el estado del registro, independientemente de
lo que haya en la entrada.

7
4 MEMORIAS ROM Y RAM

4. Memorias ROM y RAM

Hemos visto que con los registros podemos almacenar bytes de información. Pero si quisiéramos
tener memorias más grandes, deberı́amos pasar al siguiente nivel. Para almacenar datos a una escala
mucho mayor que la de los registros, tenemos las memorias, ya sean de sólo lectura o de lectura-
escritura.

Vamos a ver en esta sección cómo con tan sólo unas lı́neas de código podemos generar memo-
rias dentro de las FPGA sobre las que trabajemos. Muchas FPGA ya traen bloques de memoria
integrados, aunque también se pueden emplear los propios recursos lógicos de la FPGA para configurar
los circuitos de memoria. De todas maneras, eso puede ser hasta cierto punto transparente desde el
punto de vista del código, lo que nos incumbe a nosotros es la descripción en VHDL. Veamos pues,
cómo se describirı́an circuitos de memoria.

Figura 10: Código de una memoria RAM sı́ncrona. Fuente: propia

8
Universidad de Alicante: Grado en ingenierı́a informática.

El código que se muestra en la figura anterior es el que corresponderı́a a la descripción de una


memoria de lectura-escritura y de acceso aleatorio, lo que vendrı́a siendo una memoria RAM al uso.
La clave aquı́ es generar el espacio de memoria en base a las necesidades del diseño. Cómo se observa,
se ha declarado un tipo personalizado que representará todo el espacio de la memoria. En este caso
lo que se ha declarado ha sido un ((array)) de vectores STD-LOGIC-VECTOR. Cada vector de este
arreglo va a tener 8 bits, lo que equivaldrı́a a la capacidad de los registros que hemos estudiado antes.
Estos 8 bits serán el tamaño de las palabras en esta memoria, dicho tamaño va de acuerdo al estándar,
la mayorı́a de memoria almacenan la información en bloques de 8 bits. Por otra parte, el tamaño del
array representará la cantidad de posiciones de memoria de las que vamos a disponer. Estas memorias
las vamos a implementar de manera sı́ncrona, pero para algunos diseños puede ser más conveniente
utilizar un modelo ası́ncrono, aunque de todas maneras el código no diferirı́a mucho.

Por lo tanto, para este ejemplo concreto, tendremos 64KB de memoria disponible en este arre-
glo que hemos declarado, ya que con 16 bits de direcciones podremos referenciar 65536 posiciones
de memoria, y cada una de esas posiciones tiene 8 bits de datos (recordemos que la longitud de palabra
que usamos aquı́ será de 1 byte). El resto del código es exactamente igual que lo que hemos hecho
en ejemplos anteriores, tan sencillo cómo controlar los flancos de subida del reloj y la señal enable, y
en base a los valores de ambas entradas, se realizará una acción u otra. En el caso de la memoria de
lectura-escritura se empleará una entrada adicional selectora de operación (lectura o escritura), en el
caso de una memoria de sólo lectura no será necesario seleccionar la operación, ya que sólo se podrán
realizar lecturas sobre esta.

Debemos tener cuidado a la hora de usar los conceptos de memorias RAM y ROM. Las memorias
RAM hacen referencia al tipo de acceso del que disponen estas, es decir, hacen una diferenciación
entre acceso aleatorio y acceso secuencial. En el caso de las memorias RAM, este acceso aleatorio
permite acceder al contenido de cualquier posición de memoria en tiempo constante, pero dentro de
esta categorı́a existen tanto memorias de sólo lectura cómo de lectura y escritura, por lo tanto las me-
morias ROM con acceso aleatorio también se considerarı́an como un tipo de memoria RAM. A pesar
de esto, la mayorı́a de bibliografı́a utiliza el concepto de memoria RAM para referirse indistintamente
a las memorias de lectura-escritura volátiles, pero debemos saber que cualquier memoria que no utilice
acceso secuencial entra también en esta categorı́a.

El código para describir una memoria de sólo lectura (ROM) es muy similar al de la RAM, sólo
que en este caso no hay entrada WR, y dado que sólo se puede leer de la memoria, los datos de esta
deben venir ya inicializados, lo cuál se consigue cómo se observa en el código.

9
4 MEMORIAS ROM Y RAM

Figura 11: Código de una memoria ROM. Fuente: propia

Por último vamos a presentar los resultados de simular estas memorias. Se va a mostrar el resultado
del testeo de la memoria RAM, ya que su funcionamiento es más completo que el de la ROM. De
todos modos la simulación de la ROM serı́a muy similar, sólo que no se podrı́a observar el efecto de
las operaciones de escritura porque estas no estan permitidas, sólo observarı́amos cómo se recuperan
valores ya preestablecidos en la matriz de memoria.

Figura 12: Simulación de una memoria RAM sı́ncrona implementada en VHDL. Fuente: propia

Si observamos los valores de las señales en esta simulación, podremos apreciar varios detalles. En
primer lugar, tal y cómo se ha descrito en el código, cuando la entrada de Enable esté a 0 (desactivada),

10
Universidad de Alicante: Grado en ingenierı́a informática.

independientemente de lo que haya en el resto de entradas, la salida será Z. Esto se ha hecho ası́ debido
a que no siempre se querrán realizar operaciones sobre la memoria, pero los buses de entrada siempre
tendrán un valor, aunque sean todos 0, ya que que no se considera el estado de alta impedancia en
las entradas. Si incorporáramos la posibilidad de poner las entradas a alta impedancia, entonces la
entrada de Enable no serı́a necesaria. Pero vemos cómo usando esta entrada de activación, la memoria
sólo arroja datos a la salida cuando se desea, de manera que el bus que esté conectado a esta no estará
constantemente ocupado por ella.

El funcionamiento de la lectura y escritura en memoria se ha ilustrado de manera bastante con-


veniente aquı́. Por ejemplo, fijémonos en los primeros 5 ciclos de reloj. En el primer ciclo se realiza
una escritura en la primera posición de la memoria, la cuál se hace efectiva en el flanco de subida,
mismo momento en el que la salida deja de ser indefinida y se coloca en Z, ya que cuándo se escribe
en la memoria no se necesita recoger ningún dato de la salida de esta, y por ello se coloca en alta
impedancia. En el segundo ciclo lo que se realizará será una lectura del valor previamente registrado,
y vemos que hasta que no ocurre el flanco de subida en la segunda mitad de este ciclo, la salida no
cambia de alta impedancia al valor deseado. Se vuelve a repetir esta dinámica, pero esta vez con la
segunda posición de memoria, se escribe, se lee y se observan los valores correctos.

Figura 13: Bloques de memoria distribuida en una FPGA. Fuente: https://www.researchgate.net/


figure/FPGA-with-distributed-Block-RAMs_fig1_266881037

5. Contadores
Los contadores son sistemas secuenciales muy sencillos, que como su nombre indica, se encargan
de manejar conteos, ya sean incrementales o decrementales. En este caso, vamos a implementar una
versión ascendente y otra descendente, aunque también serı́a muy fácil añadir una entrada extra que
seleccionara si se va a incrementar o decrementar la cuenta.

En los contadores no tendremos entradas de datos, simplemente tendremos la entrada de reloj,


que será la que marque el ritmo del conteo, y una entrada de reset, que reinicie el contador. Obvia-
mente, al usar la señal de reloj cómo pauta para modificar la cuenta, este sistema será sı́ncrono.

11
5 CONTADORES

Quizá cuando pensamos en contadores el concepto de sincronismo nos resulta lo más lógico, aunque
también existen los contadores ası́ncronos y por supuesto se usan para diversos propósitos.

Figura 14: Código del contador ascendente (izquierda) y descendente (derecha). Fuente: propia

Aquı́ hemos usado una forma distinta de detectar los flancos de subida de reloj, con la sentencia
((rising-edge)), pero funciona exactamente igual que lo que hemos hecho hasta ahora. Vemos que el
código del contador incremental y el decremental sólo se diferencian en la operación que se realizará,
sumar o restar 1, y en que en el reset estableceremos a distintos valores el sistema, dependiendo del
tipo de este. Es coherente pensar que para un contador decremental, la entrada reset establecerá el
registro al valor más alto que se pueda representar, ya que a partir de él la cuenta será descendente,
y en el caso de un contador incremental, al resetear lo que se hará es establecer el valor del conteo a
0, para seguir con una cuenta ascendente.

Figura 15: Simulaciones de contador incremental (arriba) y decremental (abajo). Fuente: propia

Observamos en las simulaciones cómo se realizan cuentas ascendentes y descendentes correcta-


mente, sincronizadas con los flancos de subida del reloj. Y también se observa cómo se reinician

12
Universidad de Alicante: Grado en ingenierı́a informática.

correctamente los contadores cuando se activa la señal reset. Debemos ser conscientes de que estos
contadores, a pesar de su aparente sencillez, son una primera aproximación a las máquinas de estados,
podemos verlo cómo una máquina de estados muy básica en la que todas las transiciones son a estados
superiores o inferiores, dependiendo de la direccionalidad del conteo.

En la próxima sección partiremos de los resultados aquı́ presentados, ya que vamos a centrarnos en
desarrollar más a fondo la parte de máquinas de estados. Viendo lo sencillo que ha sido desarrollar este
contador, que a pesar de cumplir una función muy básica puede considerarse un precursor de máquina
de estados, es cómo nos damos cuenta de la potencia de VHDL. Desarrollar máquinas de estados
más complejas no va implicar un salto muy grande en dificultad respecto a lo que ya sabemos hacer,
tan sólo debemos seguir teniendo en mente que todo se reduce a saber describir de manera
correcta cómo queremos que funcione el sistema a implementar.

Figura 16: Ejemplo de diagrama de máquina de estados finitos. Fuente: https://www.hackerearth.


com/problem/algorithm/melay-machine-29344cda

En la figura de arriba se muestra un diagrama tı́pico de estados, los utilizaremos bastante en la


próxima sección. Ya sólo quedarı́a explicar las máquinas de estados para tener todos los conocimientos
necesarios para implementar la UART, hemos implementado todo tipo de sistemas sencillos y no tan
sencillos, pero definitivamente las máquinas de estados serán las que nos permitan dar el paso a los
sistemas avanzados en VHDL. Por lo tanto, la próxima sección será la última de la primera parte del
trabajo, tras esta ya vendrı́a la parte final, en la que nos centraremos más en cuestiones puramente
técnicas respecto a la implementación que en presentar conceptos sobre descripción en VHDL.

13
Sección 5: Desarrollo de máquinas de estados con
VHDL.
José Luis Mateo Pastor

1. Introducción y conceptos clave

En este capı́tulo vamos a aprender a desarrollar sistemas más avanzados mediante la construcción
de máquinas de estados finitos, también conocidas cómo autómatas finitos. Cómo hemos
explicado en el capı́tulo anterior, en estos sistemas la salida no sólo depende de la entrada actual, por
lo que son idóneos para casos en los que se necesite describir el comportamiento de un circuito en base
al valor de sus entradas y de cómo van cambiando en el tiempo. En realidad, podemos trabajar con
dos modelos principales de máquinas de estados, ası́ que vamos a explicarlos y luego entraremos más
en materia.

Diferenciaremos entre dos enfoques a la hora de implementar MEF’s (Máquinas de estados finitos):

Por una parte tenemos el Modelo de Mealy. Este modelo determina las salidas del sistema
en base a el estado actual y el valor de las entradas en ese momento. Por lo tanto habrá
que usar lógica combinacional extra para procesar las salidas.

La otra opción es el Modelo de Moore. El planteamiento de este modelo es más sencillo que
el de Mealy, ya que las salidas sólo van a depender del estado actual. En este tipo de sistemas
necesitaremos más elementos de memoria, ya que por lo general es necesario utilizar más estados
que con las máquinas basadas en Mealy.

Aún nos harán falta algunos conceptos nuevos de VHDL para poder implementar estos sistemas.
Realmente, con lo que sabemos ya (que no es poco) podemos implementar estas máquinas de estados
y la mayorı́a de sistemas que se nos ocurran, pero hay que introducir un concepto avanzado que es
fundamental para facilitar la descripción de estos circuitos.

2. Tipos enumerados

Ya vimos en el anterior capı́tulo un nuevo tipo de datos, el tipo array, el cuál usamos para im-
plementar memorias. En esta ocasión vamos a emplear el tipo enumerado para trabajar con los
distintos estados de nuestras MEF, el cuál nos será muy útil. Nos permitirá definir un tipo especial

1
3 AUTÓMATA 1

que contendrá como posibles valores los nombres de los distintos estados que queramos
usar.

3. Autómata 1
La mejor manera de aprender a describir máquinas de estados va a ser practicando, por lo que este
capı́tulo va a ser principalmente práctico, vamos a implementar diversos autómatas y realizaremos las
aclaraciones necesarias para entender su descripción. Cabe resaltar que todas las implementaciones
van a ser de sistemas sı́ncronos.

Figura 1: Diagrama de transiciones del autómata 1. Fuente: propia

El sistema que vamos a implementar en esta sección se trata de un autómata encargado de ges-
tionar el aforo de un local. Imaginemos que tenemos un farmacia, por ejemplo, y queremos controlar
el aforo debido a las restricciones sanitarias. Podrı́amos instalar un sistema que emita una luz roja
cuando el aforo esté completo (por lo tanto no se puede entrar) y que emita una luz azul mientras
quede espacio disponible para que entre más gente. El diagrama de la figura de arriba corresponderı́a
a un autómata que gestionará un aforo máximo de 5 personas, una vez hayan entrado 5 personas al
local, la luz se pondrá en rojo, y permanecerá ası́ hasta que salga alguien y deje espacio para nuevos
clientes. Cabe decir que una vez se ha completado el aforo no es posible que entre nadie más de
ninguna manera, por lo que nunca van a haber más de 5 personas a la vez en el local.

La única entrada que hay en este sistema es la señal de un sensor colocado en la puerta que detecta
cuando alguien entra (un nivel lógico alto) o sale (nivel lógico bajo). La salida será 0 o 1 dependiendo de
si queremos que la luz brille en color azul o rojo respectivamente. En este caso, el sistema sólo pondrá

2
Universidad de Alicante: Grado en ingenierı́a informática.

su salida a 1 cuando llegue al estado final (el estado 5), y será 0 en cualquier otro caso. Vemos que
se ha resaltado en verde el estado inicial, el cuál corresponderı́a al local vacı́o (todo el aforo disponible).

Cómo en este caso las salidas sólo dependen del estado en el que se encuentre la máquina, estamos
utilizando un modelo de Moore. Vamos a ver cómo se implementarı́a esto en VHDL:

Figura 2: Código del autómata 1. Fuente: propia

Pasemos a comentar esta descripción. Lo primero que llama la atención es la lı́nea anterior a la
declaración de la señal ((estado)). Dicha lı́nea trata sobre lo que hemos comentado justo antes, una
definición de un tipo enumerado, el cuál usaremos cómo tipo de dato personalizado para tratar con los
estados del autómata. Vemos que tras definir este tipo enumerado ((Estados)) con los posibles valores
que puede tomar (correspondientes a todos los estados posibles del sistema), la señal definida es de
este tipo enumerado.

Una manera sencilla de describir el diagrama de transiciones propuesto serı́a la que se observa en
el código, utilizar una sentencia case que realice las transiciones en base al valor de la
entrada y al estado actual. En este código el estado Idle corresponderı́a al estado inicial, el estado
Rojo corresponderı́a al estado final (porque enciende la luz roja) y el resto de estados tienen una
correspondencia directa con el diagrama.

3
3 AUTÓMATA 1

Figura 3: Simulación del funcionamiento del autómata 1. Fuente: propia

Por último vamos a comentar los resultados que se observan en la simulación de la figura 3. La
situación que se ha simulado aquı́ se corresponderı́a a la siguiente en un escenario real:

En primer lugar, el sensor de la puerta registra una señal lógica baja, la interpretación de esta no
es relevante, ya que en el estado inicial con la señal 0 ciclaremos la transición al mismo estado.

A continuación entran 3 personas, es decir, registramos tres señales a 1. Por lo tanto nos irı́amos
al estado 3.

Después sale un cliente, por lo que recibimos un valor 0 y retrocedemos al estado 2.

Más tarde entran otros 3 clientes, y esta vez nos irı́amos al estado Rojo, por lo que se activará
la luz roja y ningún cliente nuevo entrará hasta que salga alguno de los que ya esta en el local.

Al salir dos clientes, la luz se vuelve a poner en estado 0 (azul) y permite la entrada de gente al
establecimiento.

Por último vemos cómo si entran otros dos clientes el aforo se vuelve a completar y se activa la
luz roja hasta que uno de ellos sale.

En la simulación cambiamos los valores de la entrada cada perı́odo de reloj, pero estos cambios no
se registran en el sistema hasta el siguiente flanco de subida, ya que ası́ está definido en el process.
Tan sólo hay que tener en cuenta esto para identificar cada uno de los instantes antes descritos en la
simulación.

Ahora que ya sabemos utilizar los tipos enumerados, podemos oficialmente concluir con la parte
correspondiente al aprendizaje de VHDL, ya tenemos los conocimientos suficientes para realizar cual-
quier implementación que nos propongamos, y si nos faltara alguno, con la base que hemos adquirido
no nos costarı́a mucho incorporarlo. Y dado que no queda nada nuevo por explicar sobre VHDL, ya
sólo nos vamos a centrar en los detalles de los sistemas a implementar.

Esta máquina de estados finitos es bastante sencilla, vamos a implementar algunas un poco más
complejas, pero no mucho más. La idea fundamental ya se ha presentado, tener claros cuáles serán los
estados y las transiciones entre ellos es lo más importante. Lo demás no será otra cosa que describir
en VHDL el diagrama de transiciones de la manera que más convenga en cada momento.

4
Universidad de Alicante: Grado en ingenierı́a informática.

4. Autómata 2

El autómata que vamos a implementar en esta sección consistirá en un sistema de peaje para un
parking. Veamos a continuación cuál es el diagrama de transiciones asociado:

Figura 4: Diagrama de transiciones del autómata 2. Fuente: propia

Vamos a explicar en qué consiste este sistema. Se trata de una simplificación de la tı́pica barrera
que hay en los aparcamientos de coches. En este caso concreto, el sistema requiere que ingresemos
3¿ para que podamos pasar. El funcionamiento de la barrera es el siguiente: vamos a tener una luz
que estará en rojo cuando no se permita pasar, y se pondrá a verde cuando se haya introducido la
cantidad necesaria de dinero, indicando que ya podemos pasar. El sistema sólo admitirá monedas de
euros, ya sean de 1 o 2 ¿. Dispondremos de un botón que podremos presionar y en base al estado
en el que se encuentre la máquina, presionar el botón tendrá un efecto u otro.

Si presionamos el botón tras haber introducido los 3 euros, se abrirá la barrera y nos permitirá
pasar. Si lo apretamos antes de haber introducido la cantidad total, se nos devolverá el dinero que
hayamos introducido hasta ese punto. Si apretamos el botón sin haber introducido nada, no se llevará
a cabo ninguna acción. En el caso de que introduzcamos más de 3¿ en la máquina, se nos devolverá el
importe excedente. La luz permanecerá siempre en color rojo hasta que presionemos el botón después
de ingresar el importe necesario o si introducimos dinero de sobra (en este caso la luz se pondrá en
verde cada vez que introduzcamos una nueva moneda habiendo introducido ya el importe necesario,
avisándonos de que ya podemos pasar y que no es necesario introducir más dinero), en cuyo caso
también se nos hará una devolución.

5
4 AUTÓMATA 2

Tan sólo tendremos una entrada y una salida. Los códigos de entrada se muestran en el diagrama
de la figura 4, y representan las acciones que puede realizar el usuario, introducir monedas o presionar
el botón. De igual manera se definen los códigos de la salida, que representarán el estado de la luz y la
lógica de devolución de saldo. El bit menos significativo representará si se devuelve saldo o no, y el bit
más significativo representará si la luz brilla en color rojo o en color verde. Dado que aquı́ la salida
del sistema depende tanto del estado que tenga el autómata en ese momento cómo de la entrada que
se le proporcione, en este caso tenemos un modelo de Mealy.

Veamos a continuación cómo serı́a la implementación en VHDL:

Figura 5: Código del autómata 2. Fuente: propia

Vemos que el código es muy similar al del Autómata 1, la única diferencia en este caso es que
empleamos condicionales extra para determinar las salidas en base a las entradas dentro de la lógica
de cada estado, de manera que cada salida se asocia a una combinación de estado-entrada concreta.

Mediante estos dos ejemplos hemos mostrado claramente la diferencia entre los enfoques de Mealy
y Moore. Mientras que en el primer autómata que hemos implementado (el del sistema de aforo) la
lógica que se seguı́a era la de un autómata de Moore, en el cuál la salida sólo la determinaba el estado
actual, y tan sólo el estado siguiente dependı́a de la combinación de entrada y estado actual, en este
segundo autómata hemos seguido una lógica de Mealy, donde tanto la salida cómo el estado siguiente

6
Universidad de Alicante: Grado en ingenierı́a informática.

dependen de la combinación de entradas y estado actual.

Figura 6: Simulaciones del autómata 2. Fuente: propia

Ahora vamos a presentar los resultados de un conjunto de simulaciones que se han realizado, las
cuales representan varios casos prácticos que se podrı́an dar en el comportamiento de la máquina de
estados:

Caso 1: Esta simulación consiste en un supuesto en el que un usuario inicialmente presiona el


botón, para observar que no ocurre nada (la máquina permanece en estado Idle). Tras darse
cuenta de que debe introducir 3¿, procede a introducir 3 monedas de 1¿, y tras esto pulsa el
botón de nuevo, y esta vez observa cómo la luz se pone en verde, indicándo que puede pasar.

Caso 2: Igual que en la simulación anterior, el usuario presiona el botón antes de haber introdu-
cido el importe necesario, y ve que la luz permanece en rojo. Luego introduce el importe total
(una moneda de 1¿ y otra de 2¿), pero se confunde e introduce otra moneda de 1¿, por lo
que al exceder el importe necesario la máquina le realiza una devolución y pone la luz en verde
indicándole que ya se ha ingresado el importe necesario y que puede pulsar el botón para pasar.
Tras esto, se presiona el botón, la máquina vuelve a iluminarse en verde y se desbloquea el paso.

Caso 3: Aquı́ el usuario introduce dos monedas de 2¿, por lo que la máquina realiza una devo-
lución y pasa al estado de Pass, a la espera de que se presione el botón. En cuanto se pulsa el
botón la luz se pone verde y el cliente pasa, sin la necesidad de realizar ninguna devolución, ya
que se ha realizado previamente.

Caso 4: Aquı́ lo que se ha probado es el efecto de pulsar el botón cuando se ha introducido dinero
pero sin llegar al importe objetivo, causando una devolución del importe introducido hasta ese

7
5 AUTÓMATA 3

momento y un reinicio de la máquina (vuelta al estado Idle).

5. Autómata 3

Figura 7: Diagrama de transiciones del autómata 3. Fuente: propia

Aquı́ tenemos otro autómata de Moore.Luego veremos una pequeña desventaja de usar este enfo-
que, cuando presentemos los resultados. Pero primero vamos a introducir los detalles acerca de esta
MEF.

Se trata del sistema de una grabadora ficticia que hemos diseñado. Esta grabadora tendrá tres
botones: el de Rec,el de Pause y el de Save. El botón de Rec sirve para que al pulsarlo comience la
grabación, y el botón de Pause sirve para pausar la grabación (cómo su nombre indica). Por último,
con el botón de Save podemos guardar la grabación una vez hayamos terminado con ella. La opción
de no pulsar ningún botón también se contempla en este caso, por lo que hemos necesitado 2 bits para
codificar las cuatro posibilidades de la entrada. La salida es muy sencilla, el sistema graba o no graba.
Cuando graba, el bit de salida se coloca a 1, y mientras la salida esté a 0, la grabadora no estará activa.

Inicialmente el sistema queda a la espera de que se pulse el botón de Rec. Una vez iniciada la
grabación, se puede cancelar presionando el botón de Save o se puede pausar con el botón de Pause.
Una vez la grabación esté pausada, podemos reanudarla pulsando otra vez el botón de Rec o podemos
terminarla presionando Pause por segunda vez. Una vez la grabación esté terminada, la podremos
grabar pulsando Save, o podremos reanudarla presionando Rec dos veces. Mientras no se pulse

8
Universidad de Alicante: Grado en ingenierı́a informática.

ningún botón el autómata permanecerá en el mismo estado en el que se encuentre en


ese momento.

Figura 8: Código del autómata 3. Fuente: propia

Respecto a la descripción VHDL de este autómata, no hay nada a remarcar. Lo único que puede
llamar la atención es que el estado End se ha llamado Terminate en el código, ya que End es una
palabra reservada en VHDL y no se puede usar por razones evidentes.

Figura 9: Simulación del autómata 3. Fuente: propia

Lo único que hay que comentar de la simulación es una pequeña particularidad que se da debido
al enfoque seguido para implementar el autómata. Dado que la salida sólo depende del estado en el
que se encuentre el sistema en un momento dado, los cambios en la salida no se dan en el momento
exacto de la transición, si no que se producen en el primer flanco de subida que coincida con el nuevo
estado. Esto lo podemos intuir al observar el código, ya que el valor de la salida se establece de manera
incondicional en cada estado.

Por lo tanto, cuando queremos pasar de Grabando a Pausa o viceversa, vamos a tener un retraso

9
6 AUTÓMATA 4

de un ciclo de reloj, debido a que cuando se activa la señal de grabación se produce el cambio de
estado pero no se produce un cambio en la señal de salida hasta el siguiente flanco de reloj, que
es cuando se refleja realmente la salida asociada al nuevo estado. Esto se puede apreciar bastante
bien en la simulación que se muestra. Podrı́amos solucionar esto modificando la señal de salida en
cada condicional de los casos, lo cual nos permitirı́a anticipar el cambio de la señal de salida, ya que
estaremos estableciendo la salida correspondiente al estado siguiente en el momento de la transición.
Es decir, introduciendo un cambio del Output junto con el cambio de estado, pero esto se aproximarı́a
más a un enfoque Mealy, ya que deberı́amos tener distintos valores de salida para cada condicional,
dependiendo de la entrada y el estado actual.

6. Autómata 4

Figura 10: Diagrama de transiciones del autómata 4. Fuente: propia

El último autómata que vamos a implementar en esta sección será uno que representa un sistema
de transmisión. Este transmisor funcionará en base a un protocolo definido y tendrá un sistema de
control que será la salida del autómata. La entrada del sistema será la lı́nea de transmisión Tx, la
cuál realmente es lo que el transmisor está enviando, pero será lo que usaremos para determinar las
transiciones de los estados. El sistema de control que representa la salida será un sencillo indicador
que se pondrá a 1 cada vez que una transmisión se haya completado con éxito.

La lı́nea de transmisión permanecerá a 1 en su estado de reposo, y para iniciar la transmisión


deberemos ponerla a un nivel lógico bajo (el bit de start). Tras este ciclo de reloj en el que la lı́nea
de transmisión a 0 indica el inicio de secuencia, enviaremos 8 bits en cada ciclo de reloj y un bit de
paridad, que se pondrá a 1 o a 0 dependiendo de si el número de bits a 1 en la transmisión es par o

10
Universidad de Alicante: Grado en ingenierı́a informática.

impar respectivamente. Este bit de paridad es un método muy sencillo de detección de erro-
res, ya que el receptor luego puede comprobar que el recuento de 1’s es coherente con el bit de paridad.

Por último, se finalizará la secuencia de transmisión con dos bits de parada establecidos a un nivel
lógico alto. La señal de control se pondrá a 1 cuando se haya completado la transmisión y se mantendrá
en 0 en caso de que la transmisión haya sido errónea.

Figura 11: Simulación del autómata 4. Fuente: propia

En esta simulación podemos observar una secuencia correcta de envı́o del transmisor. Hay que
fijarse en que en esta ocasión, al contrario que en el autómata anterior en el que tenı́amos un pequeño
retardo en el cambio de la señal de salida, las transiciones anticipan este cambio. Si observamos con
atención, vemos que el bit de control se pone a 1 con el segundo flanco de reloj que coincide con
un valor alto de la lı́nea de transmisión. Esto es porque en este caso hemos hecho las cosas un poco
diferentes en el código, vamos a verlo:

Figura 12: Código del autómata 4. Fuente: propia

Vemos que esta vez el código es un poco distinto al de los autómatas anteriores. Hemos separado en
dos bloques la lógica del estado siguiente y la de el registro de estado, utilizando dos process distintos.

11
6 AUTÓMATA 4

Tenemos que tener muy presente que a pesar de que un process está descrito después de otro,
ambos funcionan de manera concurrente. Por lo tanto cada vez que el process correspondiente
a la lógica del estado siguiente actualiza el estado del sistema, el process del registro de estado conso-
lidará este cambio en el siguiente ciclo de reloj (con el flanco de subida).

De ahı́ que observemos el fenómeno antes discutido, la salida se va a actualizar más rápido que
el estado del sistema, pero en este caso eso no es problemático, ya que ası́ es cómo se ha diseñado.
Otra cosa a destacar es que, a pesar de que en un primer vistazo el sistema pueda parecer tipo Moore
observando la descripción, en realidad es un autómata de Mealy, ya que para el estado de Stop, ten-
dremos valores distintos de salida en función del valor de la lı́nea de transmisión.

Ese autómata nos va a servir bastante en para la parte final del trabajo, ya que el protocolo
que se ha seguido no es arbitrario. Lo que hemos hecho ha sido nada más y nada menos que un
emisor de tramas de datos siguiendo el protocolo UART. Obviamente el sistema completo es un
poco más complicado que esto, pero lo veremos más adelante. La idea principal queda presentada con
este ejemplo, aunque en este caso hemos usado dos bits de parada cuando en otros sistemas
se usa sólo 1 bit, dependiendo de las necesidades y de varias implicaciones técnicas.

Figura 13: Paquete de datos UART. Fuente: https://www.electronicshub.org/lpc2148-uart-


tutorial

También debemos tener en cuenta que en este caso, el transmisor se ha simulado utilizando la
frecuencia de reloj por defecto del ISE, pero en sistemas UART reales se usan frecuencias especı́ficas
que muchas veces se pueden seleccionar de entre varios valores estandarizados, por lo que lo primero
que haremos para implementar el sistema UART completo será tratar esta cuestión.

12
Proyecto Final: Implementación de una UART.

José Luis Mateo Pastor

1. Introducción

Para concluir con este trabajo, se va a implementar un sistema complejo, una UART. Las siglas
de UART provienen de Universal Asynchronous Receiver Transmiter, es decir, un transmisor-receptor
ası́ncrono, y es un protocolo que se usaba para comunicar sistemas digitales entre sı́, una especie de
interfaz. La parte de Universal hace referencia a que es un protocolo estandarizado, y hace bastantes
años se usaba mucho, pero hoy en dı́a está prácticamente obsoleto. La forma en la que funciona este
sistema es conectando los dos elementos de la comunicación entre sı́ mediante tres cables: una tierra
común, un cable de transmisión y un cable de recepción. Ambos elementos de la comunicación podrán
recibir y transmitir simultáneamente, por lo que la comunicación es full-duplex.

Cómo hemos comentado en la sección anterior, para comunicar dos sistemas entre sı́ necesitamos
establecer una tasa de transferencia de la información que sea la misma en ambos extremos. Lo normal
es que no siempre se funcione a una frecuencia fija, si no que se trabaja con un conjunto de frecuencia
estándar y se pueden usar unas u otras dependiendo de la configuración del sistema. Es por eso que
debemos hacer que la frecuencia a la que va a funcionar nuestra UART sea configurable, ya que si
hacemos que dependa del reloj del sistema sobre el que funciona no podremos elegir una frecuencia de
trabajo adecuada, sobre todo teniendo en cuenta que al ser un protocolo bastante antiguo, funciona
a frecuencias bastante lentas para lo que existe hoy en dı́a.

Es por ello que lo primero que vamos a implementar es un divisor de frecuencia, que nos per-
mitirá convertir una señal de reloj a otra más lenta según nuestras necesidades. En el contexto de
estos sistemas de comunicación, se suele usar la unidad del Baudio para hablar de las velocidades de
transferencia. Los Baudios son una unidad que hace referencia a lo rápido que transmite información
un sistema, pero a nosotros en este caso nos basta con saber que un baudio va a equivaler a un bit por
segundo (bps). Y dado que en el protocolo UART se transmite un bit por cada ciclo de reloj, vamos a
tener una correspondencia directa entre baudios y hercios, pero esto no tiene por que ser ası́ en otros
sistemas.

1
2 DIVISOR DE FRECUENCIA

2. Divisor de frecuencia

Hemos dicho que necesitamos recurrir a alguna solución que nos permita seleccionar la cantidad de
baudios a los que vamos a transmitir y recibir la información en el sistema. Una manera de solventar
esto es utilizando un divisor de frecuencia configurable. Este recurso nos permitirá seleccionar una
tasa de transferencia en base a una frecuencia de reloj base, que será la del reloj del sistema en el
que implementemos la UART. En este caso, tanto los baudios cómo la frecuencia base del sistema son
constantes que podemos alterar dependiendo del entorno y las necesidades que se presenten, por lo
que el sistema es completamente adaptable a los requerimientos que sean necesarios.

En este caso vamos a usar una frecuencia base de 100MHz, que es la que viene por defecto en el
proyecto de ISE que se ha creado, pero si la velocidad del reloj fuera otra, podrı́a cambiarse fácilmente
modificando la constante asociada NATIVE-RATE. La tasa de transferencia objetivo se establece en
la constante BAUD-RATE, y podemos establecer el valor que necesitemos. El sistema de división de
frecuencia es bastante sencillo, se trata de un contador que controla la relación entre la frecuencia
base y la frecuencia objetivo, cada vez que el contador alcance una cantidad determinada por esta
relación se producirá un evento en la señal de nuestro reloj artificial. De esta manera, si por ejemplo
quisiéramos generar una señal de reloj que vaya a la mitad de la frecuencia de la original, cada dos
ciclos del reloj base, nuestro reloj artificial completarı́a un ciclo.

Figura 1: Código del divisor de frecuencia. Fuente: propia

2
Universidad de Alicante: Grado en ingenierı́a informática.

Figura 2: Varias simulaciones del divisor de frecuencia. Fuente: propia

Aquı́ se muestran varios casos de prueba para el divisor de frecuencia. Hay que tener en cuenta
que en realidad la señal de reloj artificial que genera nuestro divisor no se comporta exactamente
igual que la del reloj nativo. En realidad nuestro reloj modificado completa un ciclo en cada flanco
(ya sea de subida o bajada), por lo que cada vez que esta señal cambia de 0 a 1 o viceversa se está
completando un ciclo nuevo. No debemos interpretar esta señal como un pulso de reloj al uso, ya que si
lo hiciéramos ası́, entonces tendrı́amos una frecuencia 2 veces más lenta que la objetivo. Simplemente
se han presentado los resultados ası́ para que sean más visuales, pero hay otras alternativas a esta
implementación. Muchos divisores emiten un pulso de un único ciclo de reloj base para indicar que
ha transcurrido un ciclo del reloj con frecuencia dividida, cómo sea, mientras tengamos claro cómo
utilizar la señal de reloj generada para el control del sistema no deberı́a haber problemas.

Obsérvese, por ejemplo, la última simulación de la figura 2. Esta simulación corresponde a una señal
de reloj artificial generada a 9600 Baudios (9600Hz en este caso). Para una frecuencia de 9600Hz, el
tiempo que corresponderı́a al perı́odo de la señal de reloj es de aproximadamente 104000 nanosegundos,
o 104 microsegundos. Si observamos la lı́nea de tiempo de la simulación, el cambio en la señal del reloj
divisor se da justo al transcurrir estos 104 microsegundos, por lo que funciona correctamente. Si
cometiéramos el error de pensar que este flanco de subida de la señal es una transición al segundo
semiciclo del pulso, estarı́amos interpretando esta señal de reloj a una frecuencia de la mitad de la que
esperamos, en este caso 4800Hz. Por eso debemos tener muy presente que los ciclos de nuestro reloj
divisor se completan con cada flanco, no debemos interpretarlo como un pulso periódico, ya que aquı́
los ciclos se dan entre dos flancos independientemente de si son de subida o bajada, mientras que en
el reloj original los ciclos se dan entre dos flancos de igual carácter (dos de subida o dos de bajada).
En nuestro sistema de reloj modificado, la señal cambia de estado (oscila) por cada ciclo completado.

3. Diseño del transmisor

Ahora vamos a pasar a la implementación del transmisor. Dado que VHDL nos permite describir
sistemas de forma modular, vamos a describir el transmisor y el receptor por separado. Lo primero
que tendremos que decidir será la configuración de la entidad que va a representar el transmisor, es
decir, que entradas y salidas va a tener nuestro sistema.

3
3 DISEÑO DEL TRANSMISOR

3.1. Configuración de la entidad

Empecemos por definir las salidas, que en este caso van a ser los más sencillo. A priori, la única
salida aparentemente imprescindible es la de la lı́nea de transmisión, sobre la que iremos enviando
los bits del buffer uno a uno en cada ciclo de reloj. Sin embargo, se ha añadido otra salida que nos
servirá para indicar al sistema que interactua con la UART si la lı́nea de transmisión está o no está
disponible. De este modo, mientras la salida que controla la disponibilidad esté a 0, no se podrá iniciar
otra transmisión, habrá que esperar a que la transmisión en curso termine y la señal se ponga a 1.

Respecto a las entradas, tendremos naturalmente una para el reloj. A parte de esta tendremos una
entrada que será la que inicie una secuencia de transmisión, y por último tendremos la entrada sobre
la que se volcarı́a el dato a transmitir, nos llegarı́an los 8 bits de información desde un buffer (o desde
donde proceda) y los almacenarı́amos en un registro de desplazamiento que será el que luego entregue
bit a bit la secuencia a la lı́nea de transmisión.

Figura 3: Diagrama de caja negra de nuestro transmisor. Fuente: propia

Analicemos el diagrama.La entrada Init de la entidad será la que active la secuencia de transmisión,
pero sólo activará una nueva secuencia si la salida Available está a 1, ya que esta será la que indique
que el bus Tx no está ocupado en ese momento. Hay que tener en cuenta que la salida Available
realmente es una realimentación al sistema que está utilizando la UART, no es una salida del sistema
como tal. La salida Tx por otra parte si es una salida, tanto de la UART como del sistema, ya que
está pasando información al otro extremos del enlace de comunicación (al receptor en este caso).

3.2. Configuración de la arquitectura

Vamos a decidir que elementos funcionales vamos a necesitar para que el sistema funcione correc-
tamente. En primer lugar, lógicamente necesitaremos el divisor de frecuencia para que la transmisión
se realice a los baudios establecidos. Dado que ya tenemos descrito este divisor de frecuencia, una

4
Universidad de Alicante: Grado en ingenierı́a informática.

cosa menos de la que debemos preocuparnos. Por otra parte necesitaremos un registro de despla-
zamiento, que cómo se ha mencionado previamente, usaremos para volcar ahı́ toda la información
del dato de 8 bits, y que irá emitiendo bit a bit cada componente de este mediante los desplaza-
mientos que realizará. Por último, necesitaremos un sistema de control que maneje la secuencia de
transmisión de manera correcta de acuerdo al protocolo establecido. Parece evidente pensar que una
máquina de estados finitos es la solución idónea para esto, y dado que ya hemos implementado una
solución parecida en la anterior sección, no nos va a costar mucho adaptar la lógica para este problema.

Por lo tanto, dado que ya hemos implementado registros de desplazamiento en secciones anterio-
res y el divisor de frecuencia ya lo tenemos, lo que resta ahora mismo es definir el autómata que
corresponderá a nuestro sistema de control de transmisión.

3.3. Definición del sistema de control

El autómata que vamos a utilizar para este transmisor es prácticamente el mismo que hicimos en
la sección de las máquinas de estados. En este caso, nuestra máquina de estados será de tipo Moore,
ya que la salida sólo va a depender del estado en el que el sistema se encuentre en ese momento. La
salida en este caso será un bit que indicará si el sistema de transmisión está o no está ocupado. Por
ello, el único momento en el que este valor sea 0 (el sistema está libre) será cuando este se encuentre
en estado Idle, que es el estado de reposo. En base a esta salida, obtener el valor de la señal Available
será muy sencillo, tan sólo habrá que negar la salida del autómata. No se han definido las entradas
para las transiciones del autómata porque lo único que interesa mostrar realmente es la secuencia de
estados y su salida asociada. Asumiremos que las entradas siempre serán correctas, ya que se emitirán
según el protocolo, por lo que nunca emitiremos un bit que no corresponda al estado del sistema.

Figura 4: Diagrama de transiciones del sistema de control del transmisor. Fuente: propia

Aquı́ vemos el diagrama de estados, correspondiente a una trama con un bit de inicio, 8 bits de
datos, un bit de paridad y 2 bits de parada.

5
3 DISEÑO DEL TRANSMISOR

3.4. Implementación del transmisor

Ahora vamos a implementar el sistema transmisor de la UART, y una vez tengamos este, imple-
mentaremos el receptor, que seguirá una filosofı́a parecida.

3.4.1. Prototipo inicial (sin divisor de frecuencia)

Vamos a ver el código de una primera versión sin selección de tasa de transmisión, ya que para la
simulación nos vendrá bien probar primero con un sistema que vaya sincronizado al reloj principal:

Figura 5: Código del transmisor. Fuente: propia

6
Universidad de Alicante: Grado en ingenierı́a informática.

Figura 6: Código del transmisor. Fuente: propia

Y a continuación se muestran los resultados de una simulación del transmisor.

Figura 7: Simulación del envı́o de 2 tramas UART sin divisor de frecuencia. Fuente: propia

7
3 DISEÑO DEL TRANSMISOR

Vamos a comentar estos resultados. Cómo se puede observar en la figura 7, se ha añadido una
((leyenda)) que utiliza códigos de colores para definir mejor las secuencias de las transmisiones, de
forma que es mucho más sencillo interpretar los valores de la simulación.

Vemos que en la simulación se han enviado dos secuencias completas de datos, las cuales se han
disparado mediante la señal de Init. Cuando la entrada Init se pone a 1 y el bus está disponible
(no está transmitiendo nada en ese momento y por ende la señal de control Available está a 1), el
sistema comenzará una secuencia de transmisión con el dato de 8 bits que haya en el registro en ese
momento. Por lo tanto, primero deberemos colocar el dato en el registro y luego iniciar la secuencia
de transmisión, que enviará dicho dato.

Vemos que el estado natural de la lı́nea Tx, su estado de reposo, es a un nivel lógico alto, y cuando
esta se pone a 0, es un indicador de que una secuencia nueva se va a transmitir. La señal de control
Available estará a 1 siempre y cuando el sistema esté en estado de reposos o Idle (antes o después de
una transmisión completa, cuando la lı́nea Tx está a 1 también). Se puede apreciar cómo una vez ini-
ciada la transmisión, los cambios en la señal Init se descartan, hasta que no tengamos el bus disponible
otra vez no podremos transmitir, por lo que da igual las veces que entreguemos un 1 en la entrada Init,
si una secuencia de transmisión está en curso, se ignorará. Los cambios en el registro de datos también
se ignorarán mientras estemos transmitiendo, aunque se ponga un valor nuevo a la entrada de Da-
tos, lo que se seguirá transmitiendo es el valor de esta en el momento en el que se inició la transmisión.

Volviendo al código, no hay realmente nada que destaque mucho. Si que es cierto que en este caso
el registro de desplazamiento que se ha usado es de tipo PISO (carga paralela y salida serie), pero
realmente la implementación se ha basado en lo que ya habı́amos hecho (sacar siempre el bit de menor
peso y desplazar el resto del contenido). Hay que resaltar que en este caso, la lógica de control del
bit de paridad se ha implementado en el propio sistema del transmisor, luego se comentará más sobre
esto. El resto del código es una máquina de estados más o menos grande que tendrá en cuenta todas
las condiciones necesarias e implementará la secuencia correspondiente al protocolo. En este caso,
claramente estamos trabajando con una Máquina de Mealy, ya que las entradas afectan a las salidas.

3.4.2. Elección del sistema de control de errores: Paridad

Antes hemos comentado que la lógica de paridad se implementa sobre el sistema UART. Sabemos
que este bit de paridad se usa para controlar los errores, y a pesar de que es un método muy sencillo,
es efectivo en determinadas situaciones de error. Por ejemplo, si enviamos una trama con 3 bits a
1, el bit de paridad será 0. Ahora imaginemos que la trama se corrompe por cualquier motivo, y el
receptor recibe un paquete con 4 bits a 1 (en vez de los 3) del paquete original. Si tenemos la suerte
de que el bit de paridad se ha mantenido correcto y no se ha corrompido también, el receptor podrá
ver que tiene un número par de bits a 1, pero el bit de paridad le indica que estos deberı́an ser un
número impar, por lo que es capaz de darse cuenta de que ha habido un error. No es el sistema más

8
Universidad de Alicante: Grado en ingenierı́a informática.

sofisticado, pero es el que usa el protocolo UART.

En nuestro caso, calculamos el bit de paridad sumando todos los bits a 1 que se emiten y realizando
la operación de módulo 2. Esta operación nos da el resto de dividir la cantidad de bits con valor 1
entre 2. Por lo tanto, si el resto da 0, tendremos un número par de bits a 1, y ası́ es cómo utilizamos la
operación del módulo. En esta implementación estamos usando una paridad impar, pero si usáramos
paridad par, tendrı́amos que establecer el bit de paridad a 1 cuando el número de bits a 1 fuera impar
(al revés de cómo lo hemos hecho).

3.4.3. Implementación con selector de baudios

Ya tenemos un prototipo operativo que funciona a la frecuencia nativa del reloj del sistema. Ya sólo
nos falta implementar una versión un poco más completa en la que podamos seleccionar la frecuencia
a la que funcionará el transmisor.

Ahora vamos a mostrar el código final del transmisor, con el divisor de frecuencia integrado. El
código no es muy diferente al prototipo que hemos presentado, tan sólo ha habido que hacer algunos
cambios a la lógica de control y añadir el sistema de selección de frecuencia (e integrarlo con el sistema
de control). Veamos cómo quedarı́a el código y luego lo comentamos un poco.

Figura 8: Fragmento 1 del código final del transmisor. Fuente: propia

9
3 DISEÑO DEL TRANSMISOR

Figura 9: Fragmento 2 del código final del transmisor. Fuente: propia

Podemos comprobar que hemos hecho cambios tanto en la entidad como en la arquitectura. Hemos
añadido una nueva salida que nos servirá para observar el comportamiento del reloj artificial que usa-
remos. Esta salida no se usará para otra cosa que para mostrar de manera más clara cómo funciona
todo en las simulaciones. También vemos que hemos añadido nuevas señales y constantes, las cuales
usaremos para implementar la lógica del reloj artificial. Cómo usamos una entrada de habilitación
para el reloj artificial, dedicaremos una señal para este propósito.

Un cambio importante que hemos hecho ha sido trasladar la lógica de inicialización de la secuen-
cia a otro proceso independiente. Esto se ha hecho ası́ porque era conveniente asociar la lógica de
inicialización de la transmisión con la lógica de habilitación del reloj, de manera que el reloj artificial
sólo empezará a funcionar cuando se inicie una nueva transmisión, para asegurarnos de que el sistema
está bien sincronizado. Otro cambio importante es que ahora la lista sensible del process del sistema
de control depende exclusivamente de la señal del reloj artificial, y desencadenará las transiciones
con cada evento del reloj, a diferencia de cómo lo hacı́amos antes con el reloj nativo, en el que el
desencadenante era el flanco de subida. Esto está relacionado con lo que se ha explicado antes, el reloj
artificial completa un ciclo en cada cambio de estado.

Dado que ahora la secuencia de transmisión sólo se iniciará cuando el reloj artificial está habilitado,
y este a su vez sólo se habilitará cuando la entrada Init esté a 1, no será necesario comprobar el valor

10
Universidad de Alicante: Grado en ingenierı́a informática.

de Init en el estado Idle, ahora siempre se realizará un salto incondicional desde Idle a Start.

Figura 10: Simulación del envı́o de 2 tramas UART con divisor de frecuencia. Fuente: propia

Vamos a comentar ahora el resultado de esta simulación. En primer lugar, debemos tener en cuenta
que estamos realizando la simulación a 100Mhz, y que hemos seleccionado una frecuencia de transmi-
sión de 50Mhz (o 50.000.000 de Baudios). Por ello, cada dos ciclos del reloj nativo se completará un
ciclo del reloj artificial, debemos tener esto presente.

Hay otro detalle importante que hay que tener en cuenta, y es que con esta implementación, la
señal de Init se deberá mantener activa por un ciclo completo del reloj artificial, no del nativo, para
que se inicie la transmisión. En este caso, si sólo pusiéramos a 1 Init durante un ciclo de la simulación,
no se iniciarı́a la secuencia, hay que mantenerlo a 1 durante dos ciclos de simulación para que funcione
(dos ciclos de simulación en este caso, ya que el reloj artificial va 2 veces más lento que el nativo, pero
dependiendo de la frecuencia que establezcamos esto variará lógicamente). Se observa muy bien que
mientras no se inicie una secuencia de transmisión, el reloj artificial permanece inactivo, porque está
inhabilitado.Cada vez que se inicie una nueva secuencia el reloj artificial empezará a oscilar otra vez.

Hay un pequeño desfase entre la habilitación del reloj y el inicio de la secuencia, pero esto no
tiene demasiada importancia, ya que el sistema transmite correctamente a la frecuencia deseada. Si
nos fijamos bien, cada bit transmitido se envı́a con un periodo de dos ciclos nativos, por lo que
efectivamente se está dividiendo la frecuencia de manera correcta.

4. Diseño del receptor

Lo último que queda por hacer es describir el receptor. Vamos a ver que la descripción VHDL será
muy similar a la del emisor, ya que el sistema de control es el mismo (seguimos usando el protocolo
UART). Lo único que cambiará un poco serán los elementos funcionales del sistema y cómo interac-
tuan entre ellos.

Vamos primero a preparar los detalles de la implementación y luego pasamos al código:

11
4 DISEÑO DEL RECEPTOR

4.1. Configuración de la entidad

Figura 11: Diagrama de caja negra de nuestro receptor. Fuente: propia

Cómo vemos en la figura 11, en este caso vamos a tener una entidad ligeramente menos compleja
que la del transmisor. Esta vez no necesitaremos un entrada Init, ya que este sistema receptor estará
a la espera del bit de inicio de secuencia, y en cuanto reciba este por el bus Rx, comenzará el proceso
de recepción de los datos.

Seguiremos teniendo la salida Available, pero en este caso esa señal representará otra cosa, ahora
hablaremos de ello un poco más. La entrada Rx, cómo ya probablemente se ha intuido, será la lı́nea
de recepción de los datos del sistema. Y por último, tendremos una salida Data, en contraposición a
la entrada data del transmisor.

4.2. Configuración de la arquitectura

La principal diferencia que habrá en la arquitectura del receptor es que en este la secuencia de
control se lanza automáticamente, no es necesario que un agente externo de una señal de Init para
comenzar el proceso, tan sólo tendrá que recibir un bit de inicio (bit Start) para comenzar el proceso.
Por otra parte, el registro de desplazamiento que vamos a utilizar aquı́ será uno de carga serie y salida
paralela, ya que vamos a ir recibiendo los bits de datos uno por uno y sacaremos todo le byte completo
de información por la salida Data cuando la secuencia haya terminado.

Aquı́ también utilizaremos el divisor de frecuencia, ya que debemos hacer que emisor y receptor
trabajen con los mismos baudios para evitar errores temporales. En este caso, el reloj artificial se
habilitará en cuanto se reciba el bit de Start ya se deshabilitará tras completar una secuencia de
recepción, hasta el inicio de la siguiente.

12
Universidad de Alicante: Grado en ingenierı́a informática.

4.3. Definición del sistema de control

Como ya hemos dicho, el sistema de control va a ser el mismo que el que se ha usado para el
transmisor.

4.4. Implementación del receptor

4.4.1. Prototipo Inicial

Figura 12: Simulación de la recepción de 2 tramas UART sin divisor de frecuencia. Fuente: propia

Figura 13: Fragmento 1 del código del receptor (prototipo). Fuente: propia

13
4 DISEÑO DEL RECEPTOR

Figura 14: Fragmento 2 del código del receptor (prototipo). Fuente: propia

Lo más importante de esta implementación es el cambio en la manera de manejar el registro de


desplazamiento. En esta ocasión estamos utilizando el registro cómo un buffer que se irá rellenando
con cada bit que llega al receptor y que volcará los 8 bits en paralelo a la salida Data cuando se haya
completado la secuencia, lo que efectivamente confirma que la recepción ha sido correcta. Fijémonos
que para mandar los 8 bits a la salida se ha hecho de una manera peculiar, ya que se han invertido el
orden de estos. Esto es debido a que los bits se almacenan en el buffer al revés de cómo los recibimos a
través del bus, ya que el primer bit en llegar será el menos significativo cuando volquemos el contenido
en la salida paralela, debido al sistema de desplazamientos del buffer. Para solucionar esto ha bastado
con invertir el orden de los bits para sacarlos por Data.

4.4.2. Implementación con selector de baudios

Para no extender innecesariamente esta sección no se va a continuar con la implementación del


receptor, ya que es prácticamente lo mismo que el emisor, por lo que tan sólo habrı́a que integrar el
reloj artificial en el prototipo que ya tenemos y obtendrı́amos un receptor sincronizable con el emisor
seleccionando los baudios a usar. Hay que destacar que la frecuencia que se ha usado en la simulación
del emisor no es una frecuencia operativa real, ya que se usan frecuencias mucho menores, tan sólo se
ha usado con el propósito de clarificar los resultados.

14
Bibliografı́a

Digital Logic Microprocessor Design With VDHL (Enoch O.Hwang)

Fundamentos de sistemas digitales (Thomas L. Floyd 11ª Edición)

Digital Design: An Embedded Systems Approach Using VHDL (Peter J. Ashenden)

FPGA Programming for Beginners (Frank Bruno)

Diseño de circuitos digitales con VHDL (Felipe Machado y Susana Borromeo)

Modern VLSI devices (Yuan Taur y Tak H. Ning)

Introducción a VHDL (Carlos A. Fajardo)

Principios de diseño de Sistemas Digitales (Universidad del Paı́s Vasco)

Máquinas de estados (DSEDA, Máster en Telecomunicaciones Universidad de Alicante)

Sentencias concurrentes en VHDL (DSEDA, Máster en Telecomunicaciones Universidad de


Alicante)

Universal Asynchronous Receiver-Transmitter (Wikipedia)

También podría gustarte