Síntesis Digital con Alliance

====================================
====================================

Capítulo I: Instalación de la máquina virtual

Para utilizar las herramientas de Alliance, se necesita el sistema operativo Fedora21. En este primer capítulo, te mostraremos cómo descargarlo e instalarlo en una máquina virtual usando VirtualBox. En el siguiente link puedes descargar el instalador.

https://drive.google.com/file/d/0B1e1hdYS0ptoT0RtRHltMEVCelE/view

Pasos:

1- Abrir VirtualBox.

2- Hacer click en "Nueva" para generar una máquina virtual.

3- En la sección "Nombre", escriba un nombre relacionado con el sistema operativo (ejemplo: Fed21)

4- En la sección "Tipo", selecciona la opción de Linux.

5- Elige "Fedora (32 bits)" en el apartado de "Versión".

Pasos 3, 4 y 5.

 

6- Tienes que seleccionar la cantidad de memoria RAM para la máquina virtual. Se recomienda dejar el valor predeterminado (1024 MB). Puede cambiarse después si es necesario.

 

7- A continuación, se selecciona el Disco Duro. Elige la opción "Usar un archivo de disco duro virtual existente". Hacer click en la parte derecha, donde aparece un pequeño folder, para seleccionar el archivo previamente descargado (Fed21curso).

 

8- Hacer click en "Crear".

 

9- Una vez creada, inicia tu máquina virtual utilizando la flecha verde de la parte superior.

 

10- Espera hasta que la siguiente pantalla aparezca.

 

11- Haz click en "User". Al hacerlo, se solicita una contraseña; basta con teclear enter y estará lista para usarse.

====================================
====================================
Capítulo II: Aprendiendo Linux

Las herramientas que serán empleadas para las actividades están desarrolladas en Linux, por lo que es importante conocer dicho sistema operativo, en específico su terminal, que es el área de trabajo que nos concierne.

A continuación se describen algunos comandos básicos:

 

  • ls: de la palabra list. Muestra las carpetas y archivos contenidos en la ruta específica en la que nos encontramos; útil para saber qué nuevos archivos se han generado y conocer si el archivo o carpeta que necesitamos está en la ruta actual (1).

Nota: Basta con escribit el comando "ls" seguido da la tecla enter, enseguida mostrara los archivos o carpetas que existen en el directorio en el que nos encontramos.

  • cd: de la palabra change directory. Sirve para cambiar el directorio actual. Como argumentos tiene la ruta nueva a la que nos queremos mover (cd Escritorio por ejemplo) (2). Utilizando la sintaxis cd .. podemos regresar al directorio superior (2). Colocando cd nos movemos hacia el directorio raíz.

•    cp: de la palabra copy. Nos permite copiar un archivo o directorio; es importante mencionar que cuenta con distintos argumentos útiles para realizar diversas tareas relacionadas con el copiado (4). Ejemplo $ cp Directorio_de_Archivo Nuevo_directorio

•    mv: de la palabra move. Sirve para mover o renombrar un archivo o carpeta. Para renombrar un archivo o directorio, los argumentos son el nombre actual del archivo, con su extensión, seguido del nuevo nombre con su extensión. Para mover, utilizamos el nombre actual, la ruta y el nuevo nombre (5).

•    rm: de la palabra remove sirve para eliminar archivos o directorios (6). Si se requiere eliminar un archivo basta con escribir el comando con la siguiente sintaxis rm Archivo.extension, Si se requiere eliminar una carpeta se usa el mismo comando pero se le agrega un modificador "-r" linux lo hace asi para evitar borrar archivos indeseados, la sintaxis quedaria de la siguiente manera: "rm -r Nombre_Carpeta".

•    clear: sirve para limpiar la pantalla de comandos (7).

Cabe destacar que la línea de comandos de Linux cuenta con diversos auxiliares que nos brindan información importante sobre las maneras en cómo podemos manejar los comandos: su sintaxis y argumentos. Estas herramientas son la opción --help, y los comandos info -k y man. Para emplear la primera, escribimos "--help" precedido por el archivo o comando del que buscamos ayuda. Al escribir "info" o "man" seguido de un comando, obtendremos información importante.

Otros comandos útiles:

•    less - visualizador de archivos en la terminal, sin la opción de edición. Ejemplo: less archivo.txt .Ejemplo: less Nombre_Archivo.txt, este comando muestra muestra el contenido de un archivo de texto, permite desplazarnos por el contenido del mismo con las teclas, si deseamos volver al directorio presionar la tecla "Q".

•    leafpad - editor de texto fuera de la terminal. Cuando no se acompaña con argumento, se abre el editor con un nuevo archivo sin nombre. Agregando como argumento el nombre de un archivo que no existe, este es creado y abierto por el editor. Ejemplo: leafpad Nuevo_Archivo.txt, Si por el contrario como argumento se coloca el nombre con extension de un archivo ya existente es te se muestra de inmediato fuera de la consola para editarlo.

•    cat - imprime lo que contiene un archivo de texto en consola, sin la opción de edición. La sintaxis es "car Nombre_Archivo.txt"

•    env - muestra las variables de estado, la mayoría relacionadas con Alliance. En los siguientes apartados se menciona de manera breve.

•    gimp - visualizador y editor de imágenes. Su argumento es la imagen a visualizar con su extensión.

•    En la terminal de Linux tenemos la opción de autocompletar títulos, rutas o comandos. Esto se hace escribiendo la primera o primeras letras de la palabra requerida. Cuando existen dos palabras con la misma letra, al presionar una segunda vez tabulador obtendremos sugerencias de palabras.

  • pwd: Muestra la ubicacion actual. La sintaxis es la siguiente "pwd" enseguida presionamos la tecla enter, y de inmediato mostrara la ubicacion actual.

====================================
====================================
Capítulo III: Creando carpetas compartidas

En ocasiones es resulta necesario trasladar archivos de un sistema operativo a otro, para poder compartirlo o procesar información en diferentes softwares, así que tener una carpeta compartida es de suma importancia.

Pasos para crear una carpeta compartida:

1- Encender la maquina virtual Fedora21.

2- En el menú de opciones que aparece en la parte superior derecha elegir "Dispositivos", después seguir la siguiente ruta: carpetas compartidas -> preferencias de carpetas compartidas.

3- Hacer click en "Agregar nueva carpeta compartida".

4- En la sección "Ruta de carpeta", deberás elegir el lugar donde desees colocar los archivos compartidos, te sugerimos previamente crear una carpeta en tu sistema operativo Host, que solo utilices para este proposito y que además sea facil de ubicar, es importante que el nombre de esta no contenga espacios.

5- El siguiente paso es asignar nombre a la carpeta, este tomará el nombre que le hayas dado a la carpeta previamente creada.

6- Selecciona las casillas "Automontar" y "Hacer permanente".

7- Haz click en "Aceptar".

8- Para poder visualizar y utilizar la carpeta es necesario reiniciar la maquina virtual.

9- Una vez que se haya reinciado, estará lista para usarse.

10-Para copiar un archivo de Linux a Windows utilizar el siguiente comando

     cp Archivo_a_copiar /media/sf_Nombre_de_carpeta_compartida/

====================================
====================================
Capítulo IV: Alliance

Como se mencionó en capítulos anteriores, Alliance es el software que utilizaremos para la  síntesis de circuitos integrados y sistemas diversos. Este software posee un conjunto de herramientas diseñadas con fines específicos en el proceso de síntesis, como optimización, prueba y verificación, y generación de planos. Es importante conocer las tareas que estas herramientas desarrollan en conjunto, además de los archivos necesarios para ser llevadas a cabo. Así bien, en este capítulo se describirán los archivos de partida necesarios para el proceso de síntesis y la descripción de las herramientas que lo hacen posible.

(I) Archivos iniciales:

• Archivo VHDL
El VHDL (1) es uno de los archivos iniciales para el flujo de síntesis, pues describe el   comportamiento del sistema a diseñar a nivel hardware. En él encontramos la descripción del sistema como un bloque con entradas y salidas, y cómo estas se relacionan para obtener determinado resultado. Este archivo permite, con la ayuda de ciertas herramientas, saber los tipos y cantidad de celdas estándar que se emplearán en el diseño, además de su interconexión a nivel esquemático.

 

•Archivo de distribución de conectores de entrada y salida.
Por medio de este archivo (2), configuramos la distribución de los puertos de entrada y salida en el núcleo o core de nuestro sistema. La importancia de este archivo está  en el diseño final del core, ya que en sistemas más complejos, este puede estar interconectado con otros. Este archivo tiene una extensión .ioc

 

•Archivo de patrones de prueba
Durante el flujo de síntesis se realizan ciertas etapas de verificación. Este archivo (3) contiene las señales de entrada con las que el sistema será excitado, para obtener ciertas señales de salida, con sus determinados tiempos de propagación.

 

(II) Herramientas de flujo de síntesis:

•Vasy (VHDL Analyzer for Synthesis): Este es un intérprete del lenguaje de alto nivel manejado en el archivo VHDL. Se encarga de realizar un cambio en la sintaxis de dicho lenguaje a uno de bajo nivel, con operaciones lógicas, tal como son realizadas por el hardware. Como ejemplo, la expresión (c <= a + b) es cambiada pr su equivalente con operaciones lógicas, como "or", "and" y "xor". Esta herramienta tiene un archivo de entrada de extensión .vhdl, y uno de salida de extensión .vbe. Es importante verificar este archivo de salida para observar las diferencias con el archivo original.

•Boom (Boolean Minimization): es utilizado para reducir el número de operaciones lógicas y compuertas; como su nombre lo indica, minimiza utilizando técnicas de algebra booleana. Su archivo de entrada es el generado por vasy (.vbe), y genera un archivo de salida .vbe optimizado.

•Boog (Binding and Optimization On Gates): Esta herramienta permite pasar de un lenguaje puramente descriptivo a un nivel esquemático, donde las compuertas y celdas estándar se interconectan entre sí. El archivo .vbe obtenido de "boom" será la entrada de esta herramienta, y su salida será un archivo de extensión .vst

•Loon (Local Optimization Of Nets): Ayuda a remover los problemas de fanout y retardos que existen en el sistema añadiendo buffers al mismo. Genera un archivo de salida .vst optimizado, partiendo de un archivo de la misma extensión generado por boog. En sistemas de gran complejidad, suelen existir problemas con el tiempo de propagación de la señal, y cuando nuestro sistema se conecta a otros, el fanout puede verse afectado. Esta herramienta lo corrige.

•OCP (Placer for Standar Cells): Sirve para generar un primer plano del circuito integrado. Sus archivos de entrada son el de distribución de puertos (.ioc) y el diagrama esquemático generado por "loon". Su salida es un archivo de extensión .ap. Es importante mencionar que en esta etapa del proceso de síntesis, el plano se encuentra sin conectar; las celdas se encuentran únicamente colocadas en un plano, con los conectores en el orden en que los colocamos en la distribución de puertos (.ioc). Además, se colocan celdas de relleno, con la finalidad de liberar el tráfico de información, aumentando el área del core.

•Nero (Negotiating Router): Toma el archivo generado por OCP (.ap) y las interconecta con la ayuda de la información de conexión del archivo generado por loon (.vst). Su salida genera un plano final con extensión .ap
Todos los archivos de texto generados por las herramientas pueden ser visualizados por medio de leafpad, less o cat. Para observar los planos, se utiliza la herramienta Graal, con la sintaxis: graal -l plano_a_visualizar

(III) Herramientas de verificación

Para determinar la funcionalidad de nuestros sistemas es necesario realizar ciertas pruebas durante el proceso de síntesis por medio de herramientas de verificación.

•Asimut (A Simulation Tool): su función es simular el comportamiento del sistema excitándolo con señales de entrada (del archivo de patrones de prueba) para obtener determinadas salidas. Esta herramienta tiene como archivos de entrada los patrones de prueba (.pat) y la descripción de bajo nivel generada por vasy (.vbe) o el esquemático generado por loon (.vst). El archivo de salida generado (.pat) muestra las señales con que fue excitado, además de las salidas obtenidas y, en caso de haber discrepancias, los errores encontrados.

•Cougar: esta herramienta se encarga de realizar una "inferencia": la extracción de un estructural desde el plano generado por la herramienta nero. A partir del plano final, extras las celdas y elementos y su interconexión. Su archivo de salida tiene extensión .al

•LVX (Gate Netlist Comparator): Esta herramienta toma como entradas la extracción estructural generada por cougar y el esquemático o estructural producido por loon y los compara, con la finalidad de encontrar diferencias o discrepancias entre ellos. Cuando existen diferencias, arroja un error, mientras que si no las hay, muestra un mensaje de que el proceso fue exitoso.

 

====================================
====================================
Capítulo V: Sumador

Un primer ejemplo que resulta muy ilustrativo para comenzar con la implementación de modulos en Alliance es un sumador, en este
caso será de dos entradas (A y B) de cuatro bits cada una, con una salida del mismo ancho, ya que no considera el bit de acarreo.
Partiremos de los archivos que Alliance proporciona como ejemplos.

Pasos:
1- Primero, tenemos que estar dentro de la carpeta que contiene "alliance-examples", si descargaste la máquina virtual mediante el link que aparece en el capítulo I, la carpeta mencionada estará dentro de la carpeta "\home\user\Work".

2- Tenemos que copiar la carpeta "adder4" utilizando el comando que aparece en la imagen de abajo.

 

3- Ahora, utilizaremos los archivos inciales que contiene la carpeta como plantillas, y podrán ser modificadas si así se desea.

4- El archivo más importante es el VHDL, ya que contiene la entidad y la arquitectura del módulo, es decir entradas, salidas y
como se conectan. En este archivo puedes cambiar en ancho de los buses, dependiendo lo que necesites.

 

5- Despues, abre el archivo "adder4.ioc", este contiene la información de como estarán distribuidas las entradas y salidas alrededor del core, lo puedes acomodar como mejor te convenga (top, bottom, left and right), elige con cuidado la distribución, ya que si vas a conectar este modulo con otros, tener el mejor diseño puede optimizar todo el sistema.

 

6- Sigue abrir el archivo "adder4.pat", es aquí donde se realizan las pruebas para verificar el correcto funcionamiento del módulo.

 

7- Para ejecutar las herramientas de sintesis y verificación en un solo archivo, es necesario hacer un script, que contenga todas ellas.
Como se muestra en la imagen de abajo, el script tiene que empezar con "#!/bin/bash" para que se reconozca como uno.
En la segunda línea se cambia una variable de estado, que determina el número de errores permitidos, en este caso es igual a 1, así
que al primer error, el script se detendrá. Es súper importante poner al final de cada línea doble andpersand (&&) para que una vez
que termina esa línea continue en la de abajo sin detenerse.

 

8- Finalmente ejecutamos el script con el comando que aparece en la imagen.

 

9- Espera a que se ejecuten todas las herramientas y muestre el plano en Graal.

 

10- Verifica la salida de la herramienta LVX, para comprobar que los netlist son idénticos.

 

====================================
====================================
Capítulo VI : Multiplicador Combinacional

(I) Descripción

Como en el ejercicio anterior del sumador, partiremos de los ejemplos proporcionados por Alliance, ahora revisaremos el proceso para realizar un multiplicador de dos entradas(X & Y) de 8 bits cada una, con unsa salida de 16 bits de ancho.

El primer paso es copiar la carpeta "multi8b" a nuestro directorio de trabajo "Work". Como se mostró en el capítulo anterior, es
importante posicionarnos en el directorio en el que vamos a copiar la carpeta, que en este caso es /Work. Una vez ahí, realizamos
la copia de la carpeta mediante la línea encerrada en el cuadro rojo de la imagen A. Cabe mencionar que puede sustituir
"new_multi8b" por un punto ("."), lo que hace que la carpeta se copie con el mismo nombre.

Primeramente, partimos de la imagen 2 para describir la multiplicación binaria. Supongamos que tenemos un número "a" de 8 bits el
cual queremos multiplicar por un número "b" del mismo ancho. Tal como sucede en la multiplicación decimal tradicional, multiplicamos
el primer número (el primer bit en este caso) del factor II (b) por todos los números de factor I (a). Al ser una operación binaria,
solo tenemos dos números, 1 y 0. Sabemos que el resultado de multiplicar el número por 1, resulta el mismo número, debido a que el
1 es el operador neutro en la multiplicación.

Tal como es el caso, nuestro número "a" (1 0 0 1 0 0 1 1) es multiplicado por el primer bit del número "b", que es 1. Ahora bien,
el resultado es mostrado en PP1, el cual resulta ser el mismo número "a". Como se puede notar, en operaciones lógicas la multiplicación
podría describirse como la operación "and" bit a bit. Ahora es momento de analizar el otro caso, cuando el bit de "b" es 0.

Como podemos observar, el resultado de multiplicar el segundo bit de "b" (0 en este caso) resulta en PP2=(0 0 0 0 0 0 0 0). Sin
embargo existe un detalle muy importante que hay que considerar, y es que al multiplicar este segundo elemento de "b", el resultado
PP2 se muestra "desplazado" un bit a la izquierda con respecto a PP1. Esto sucede de la misma manera en la multiplicación tradicional,
pues es el resultado de multiplicar unidades y después decenas, y así sucesivamente. Lo siguiente, si "b" fuera de dos bits, sería
realizar la suma de los productos parciales PP1 y PP2, cuyo resultado se muestra en la imagen 4.

El resultado P12 (PP1 + PP2) se muestra en naranja. Nótese el desplazamiento de PP2 con respecto a PP1. Como en la multiplicación
tradicional, las unidades "pasan" de manera directa. Dicho de otra forma, sumamos 0 a las unidades, pues este el elemento neutro
de suma. De esta manera, el resultado no se altera. Binariamente, esto se traduce a sumar el bit menos significativo de PP1 con
un 0, que se colocaría en el cuadro en blanco de la derecha. Esto, a nivel de hardware, implica que debamos "concatenar" o añadir
un 0 a PP2 a la derecha (PP2 & 0) para poder realizar la suma. De esta manera el tamaño de PP2 es de 9 bits ahora. El mismo caso
lo encontramos al sumar el bit más significativo de PP2, pues nos encontramos que se transfiere igual hacia P12. Entonces, es
necesario concatenar un 0 a la izquierda de PP1 (0 & PP1), lo que hace que sea de 9 bits.

Para este ejemplo particular, podemos ver que el bit más significativo de P12 es cero. Se pensaría entonces que puede eliminarse,
sin embargo es necesario considerar el caso en que ambos números (PP1 y PP2) fueran cuenta máxima. Es de esta manera que se llega
a la representación de la imagen 5, donde se observa que P12 es de 10 bits. Es una importante consideración se contemplar los casos
"extremos", para que no existan errores en las etapas de diseño por el dimensionamiento de los buses y demás cuestiones.

A continuación procedemos a obtener P34, realizando la suma de los productos parciales PP3 y PP4. Estos mismos resultan de multiplcar
los bits 3 y 4 respectivamente del número "b" por todos los bits del número "a". Como en el ejemplo anterior, el producto parcial
PP4 está desplazado un bit a la derecha de PP3, por lo que es necesario concatenar ceros a la derecha de los productos impares y
a la izquierda de los productos pares, como se realizó en la etapa anterior. Sin embargo es importante destacar que ahora esta
suma de productos parciales (P34) está desplazada dos bits a la izquierda de P12. Para realizar la suma de estos términos, es
necesario agregar dos ceros a la derecha de P34 (P34 & "00") y dos ceros a la izquierda de P12 ("00" & P12), partiendo del análisis
de la sección anterior.

Resulta buen ejercicio realizar el análisis de la suma de estos dos productos (R1 = P12 + P34) cuando se presenta la cuenta máxima,
para determinar así el ancho del bus. El resultado de una suma de dos números "a" y "b" de 8 bits cada uno en su cuenta máxima se
muestra en la imagen 7. Es importante destacar que para corroborar el número de bits necesarios para expresar la suma de dos números
en cuenta máxima es necesario utilizar una calculadora auxiliar, escribiendo los mayores números que pueden representarse con el
número de bits, en este caso de 8 bits (FF en hexadecimal). Multiplicar FF por FF nos da como resultado FE01, que se representa en
16 bits.

Análisis como los anteriores son necesarios para el entendimiento de todo el proceso de suma en esta arquitectura. Es necesario
estudiar el contenido del archivo multi8.vhdl, que muestra una metodología muy similar a la presentada en esta sección, la cual
va acorde a lo descrito el dicho archivo para hacerlo más entendible.

Finalmente, se muestra en la imagen 8 la manera en que se realiza la operación "and" de un bit del número "b" por todos los números
del número "a", concatenando un 0 en el bit menos significativo (a la derecha) o en el bit más significativo (a la izquierda).

De manera alternativa, dicha operación puede realizarse de la siguiente manera. El resultado es el mismo, es solo cuestión de las
líneas de código necesarias para describirlo.

 

====================================
====================================
Capítulo VI : Multiplicador Secuencial
 

Para desarrollar este ejercicio, nos basamos en el ejemplo proporcionado por Alliance, específicamente la carpeta llamada "multi8", el cual, a diferencial del multiplicador desarrollado en el capítulo anterior, es secuencial. Este multiplicador está diseñado para multiplicar dos números ("A" y "B") de 8 bits cada uno, con una salida "Result" de 16 bits.

----- I m a g e n   1 --------

Primeramente debemos de copiar la carpeta "multi8" (importante no confundir con la carpeta "multi8b") a nuestro directorio de trabajo "Work" (Imagen 1). El procedimiento a seguir es muy similar al estudiado en el capítulo VI. Para entender la manera en que esta arquitectura realiza la multiplicación de manera serial se estudiarán los módulos que conforman a la misma.

(I) Registro de corrimiento A (sra)
 

----- I m a g e n   2 --------

El archivo "sra.vhdl" describe la arquitectura de un registro de corrimiento. Como se puede observar en la imagen 2, "sra" tiene 5 entradas: "Shift", "LD", "RST", "CLK" (de 1 bit cada una) y "A" (de 8 bits); y 2 salidas: "Done" y "LSB" (de 1 bit cada una). A continuación, en la imagen 3, se muestra la descripción de flujo de datos de esta arquitectura.

 


----- I m a g e n   3 --------

Hasta ahora, en este tutorial se han estudiado ejemplos combinacionales; en este ejercicio estudiaremos una arquitectura secuencial, por lo que es necesario hacer las siguientes aclaraciones.

Para circuitos secuenciales, es necesario utilizar elementos lógicos que estén sincronizados con un reloj. Por tal motivo se utilizan "flip-flops". Estos son elementos básicos de memoria, útiles para crear registros, contadores, etc. Ahora bien, como se puede apreciar en la imagen 3 en la línea 24, encontramos la sintaxis para declarar una señal interna. Cabe resaltar que esta señal no corresponde a ningún puerto de entrada o salida del módulo, se trata de una señal auxiliar interna empleada en la descripción de la arquitectura.

En este caso, la señal se llama outsint, tiene un ancho de 8 bits y será utilizada como un registro. A partir de la línea 25 comienza una estructura llamada "Process", la cual permite al diseñador utilizar estructuras similares a las de la programación clásica, por ejemplo: "for", "if", "case", etc. En este caso, se utiliza la estructura "if". En la línea 28 se muestra su uso para describir un flanco de subida del reloj, que es llamado "event". Todo lo que sucede a continuación está en función de este "event" o flanco de subida del reloj, lo que lo identifica como un circuito secuencial.

En la línea 29 existe un condicional "if", el cual indica que si el valor de "RST" está en 1, el resultado almacenado en la señal interna outsint se llenará de ceros. Esto corresponde a la acción de reset, pues el registro se llena con ceros. Cabe mencionar que una manera alternativa de escribir dicha línea es:

if RST ='1' then outsint <= "00000000";

En este caso, la opción "others" sirve para abarcar todos los bits de la señal interna. A continuación, en la siguiente línea, se describe una instrucción "elsif", la cual "evalúa" otro caso al no cumplirse el anterior. Particularmente, si la señal de Reset está en 0, la arquitectura verifica si la señal "LD"=1, que se refiere a "load" o carga. Si esta condición se cumple, entonces la señal outsint tomará el valor que hay en la entrada A; que es lo mismo a cargar el registro con A.

En la línea 31, si no se cumple ninguna de las condiciones anteriores, se verifica que la señal Shift sea igual a 1. Si es así, entonces se realiza el corrimiento a la derecha, que representa desplazar el contenido de outsint una posición a la derecha y concatenar un 0 a la izquierda. En la imagen 4 se muestra un esquema del corrimiento.


----- I m a g e n   4 --------

Se observa que se toman los bits 7 a 1 del registro y a su izquierda se concatenan con un 0. El registro outsint es actualizado con este valor, lo que hace que se pierda el bit menos significativo (a0). En el siguiente corrimiento se repite la acción, y se concatena un 0 nuevamente, y se pierde el bit menos significativo (que ahora es a1), y así sucesivamente hasta que el registro queda en ceros. Esto sucede después de 8 corrimientos. En caso de no presentarse ninguna de las condiciones anteriores, el valor de outsint no cambia, seguirá siendo el mismo.

A continuación, en la línea 38, se inicia otro "Process". Esta parte del código nos ayuda a determinar si el registro outsint está lleno de ceros. Se vale del uso de una variable de un bit (RESULT) y una estructura "for". Los parámetros de este ciclo se definen en la línea 42. La sintaxis "oustint'range" determina hasta qué iteración se realizará el ciclo. En este caso, el rango de outsint es de 7 a 0, por lo que el ciclo se efectuará en ese rango. La variable RESULT se inicializa con 7, y dentro del ciclo su valor se actualiza con la operación OR entre el bit más significativo y su valor siguiente.

Es decir, si a7=0, RESULT=0. Si a7=1, RESULT=1. El ciclo termina cuando RESULT=1. Después el resultado de RESULT se actualiza con su mismo valor pero negado, por lo que si RESULT=1, al final del ciclo RESULT=0, y viceversa. Por último, esta variable es asignada a la salida DONE. Al ser RESULT=0, DONE=0, lo que nos indica que aún faltan términos por multiplicar. Esta señal DONE es activada cuando todos los bits que quedan en el registro outsint son 0. Una forma alternativa de producir la señal DONE es con el siguiente código que implica una compuerta NOR de 8 entradas:

DONE <= not ( outsint(7) or outsint(6) or outsint(5) or outsint(4) or outsint(3) or outsint(2) or outsint(1) or outsint(0) );

Finalmente la salida LSB toma el valor del bit menos significativo de outsint, es decir, a0. Este valor será útil para indicar a otros módulos acciones que tienen que ver con la multiplicación. Estas se explicarán posteriormente.

(II) Registro de corrimiento B (srb)
 

----- I m a g e n   5 --------

El archivo "srb.vhdl" contiene la arquitectura de otro registro de corrimiento, con ligeras diferencias con el registro sra. Este tiene cuatro entradas: "Shift", "LD", "CLK" (todas de un bit) y "B" (de ocho bits); y una salida "OUTS" (de 16 bits), tal y como se muestra en la imagen 5.
 

----- I m a g e n   6 --------

En la imagen 6 se muestra la arquitectura de estre módulo. Nuevamente se utiliza una señal interna "outsint" de 16 bits de ancho que servirá como registro dentro de un "process". En la línea 26 tenemos la estructura "if" para determinar el flanco de subida del reloj, lo que indica que este circuito es secuencial. En la siguiente linea nos encontramos con la accion "LD" que refiere a la carga de los datos de la entrada "B" en el registro. Se puede observar que existe una concatenación del bit más significativo (b7) del dato "B" siete veces, con todos los bits de "B" (imagen 7). Cabe resaltar que esto es una propuesta de este diseño en particular.

 

----- I m a g e n   7 --------
 

Podemos verificar que en caso de que la señal LD=0, entonces se evalúa si la señal Shift=1. Si esto se cumple se realiza un desplazamiento a la izquierda, con un proceso similar al estudiado en el módulo sra.

En caso de que ninguna de las dos condiciones se cumpla (LD=1 o Shift=1), entonces el valor de outsint se queda intacto. Al final el resultado de outsint se asigna a la salida OUTS.

(III) Sumador acumulador (Addaccu)
 

----- I m a g e n   8 --------

El archivo "addaccu.vhdl" describe la arquitectura de un registro acumulador-sumador. Como se puede observar en la imagen 8, consta de 4 entradas: "CLR", "LD", "CLK" (todas de un bit) y "OUTS" (de 16 bits); y una salida: "RESULT" de 16 bits.


----- I m a g e n   9 --------

La arquitectura de este módulo es similar a la de los módulos anteriores, pues consta de una señal interna que servirá como registro, llamada "resultint" de 16 bits (Imagen 9). Nuevamente en la línea 26 encontramos el flanco de subida de un reloj. Cuando este se produce se verifica si la señal "CLR" se encuentra en 1; si es así "resultint" se llena con ceros. Si no, se verifica que la señal "LD" esté en 1. Si es así el "resultint" se actualiza con el valor de "resultint" más el valor de la entrada "OUTS". Al final la salida "RESULT", toma el valor de "resultint". Es importante decir que se omite el caso cuando no se cumple ninguna de las condiciones anteriores, puesto que el registro se queda con el mismo valor.

(IV) Controlador (Controller)

 


----- I m a g e n   1 0 --------

Este módulo es el que se encarga de realizar el control de las acciones del sistema general, dando las señales de desplazamiento para los registros y los bits que indican que una operación ha concluido. El archivo "controller.vhdl" contiene los puertos de dicho módulo. Consta de 5 entradas: "STB", "CLK", "LSB", "Done" y "RST"; y 3 salidas: "Init", "Shift" y "Add". Tanto entradas como salidas son de un bit cada una (imagen 10).
 

----- I m a g e n   1 1 --------

Es importante mencionar que se hace uso de una máquina de estados para describir este módulo (imagen 11). En la línea 13 se definen los estados que esta máquina contemplará. En este caso tenemos cinco, cuyos nombres están relacionados con su función: "EndS", "InitS", "CheckS", "AddS" y "ShiftS". A continuación, definimos una señal "State", que contempla los posibles valores que pueda tomar la variable "States". Es importante mencionar que dentro de "States" definimos todos los estados, por lo que "State" recibirá el valor que estos tomen.

A continuación se manejan las salidas que servirán como señales de control para los demás módulos de acuerdo con el estado actual de la máquina. En la línea 19 observamos que la salida "Init" (importante no confundir esta variable con el estado "InitS") tomará el valor de '1' cuando nos encontremos en el estado de "InitS". De lo contrario, tomará el valor de '0'. Lo mismo sucede con las salidas "Add" y "Shift", que toman un valor de '1' cuando la máquina está en su respectivo estado ("AddS" y "ShiftS" respectivamente) y '0' cuando estemos en cualquier otro estado. Esto es de suma importancia para entender cómo funciona esta máquina de estados, pues ayudará a entender la descripción de la imagen 12.
 

----- I m a g e n   1 2 --------

En la línea 25 comienza la definición de la máquina de estados. Como se ha manejado con anterioridad, en la línea 30 nos encontramos con el evento de reloj por flanco de subida. Esto determina que nuestra máquina de estados será sensible a cambios en el reloj. La máquina de estados es descrita a continuación.

1.- Si la entrada "RST"='1', entonces la máquina de estados irá al estado "EndS".
2.- Si lo anterior no se cumple, se realiza una evaluación de la variable "State". Como se mencionó anteriormente, esta variable contempla a todos los posibles estados. Dentro de esta evaluación, el primer estado es "InitS", y he ahí el porqué de su nombre, pues es el estado de inicialización. Este estado genera una señal que indica la inicialización del sistema ("Init"='1').
3.- Una vez en el estado "InitS", se asigna "CheckS" a la variable "State". Esto quiere decir que ahora "State" modifica su valor como se muestra en la línea 35. Ahora "State"<="CheckS"
4.- Una vez en el estado "CheckS", se verifica la entrada "LSB". Si su valor es '1', entonces nos movemos al estado "AddS" de la máquina. Recordemos que "LSB" es una salida del módulo "sra.vhdl", la cual muestra el valor del bit menos significativo del dato "A" a multiplicar. Si este valor es '1', se realiza una suma, por lo que nos movemos al estado "AddS". Si el valor de "LSB" es 0, entonces verificamos que el valor de la entrada "Done"='0' (línea 39). Si esto sucede, se entiende que aún no se termina de realizar la multiplicación, por lo que se sigue al estado "ShiftS" para realizar un nuevo corrimiento. Si "Done"='1', vamos al estado "EndS".
5.- En el estado "Adds" modificamos el valor de "State"="ShiftS". Es importante decordar que en este estado, la máquina genera una señal de salida "Add"='1', que indica al sistema que se debe realizar una suma.
6.- En el estado "ShiftS" es donde se envía la señal "Shift" para que se realice un corrimiento por parte de los registros de desplazamiento.
7.- En el estado "EndS" se verifica la entrada "STB", que de alguna manera determina la ejecución de una multiplicación. Si "STB"='1', se inicia la multiplicación yendo al estado "InitS". De lo contrario, no se realiza ninguna acción.

(V) Genlib

Hasta ahora se han estudiado los módulos de manera individual, sin embargo este sistema requiere la interconexión de los módulos descritos anteriormente. Para su interconexión se utiliza una herramienta, llamada "Genlib".
 

----- I m a g e n   1 3 --------

Como se puede observar en la imagen 13, se declaran las entradas y salidas del sistema que contiene los demás módulos. En la línea 6 se declara el nombre del sistema y a continuación, de la línea 8 a 16 se describen entradas y salidas. Es importante mencionar, que cuando se trata de un puerto de un bit, basta con escribir su nombre. Sin embargo para puertos de más de un bit, se necesitan declararlos, como se muestra en la línea 11. En las líneas 15 y 16 se muestran los puertos de alimentación (vdd y vss).

Tomaremos la línea 19 como ejemplo para describir la sintaxis, que utiliza Genlib para interconectar módulos. En el primer par de comillas se escribe el nombre del módulo ("controller"); en el siguiente par de comillas se debe escribir el nombre que se asignará al módulo dentro de la interconexión. Este nombre puede y es recomendable que sea distinto al del primer argumento ("continst"). Los siguientes argumentos corresponden a los puertos de entrada y salida propios de cada módulo, y deberán escribirse en el orden en el que aparecen en el archivo de descripción estructural (.vst), como puede observarse en la imagen 14.
 

-----I m a g e n 1 4 --------

Tal como aparecen los conectores en el archivo estructural es como hay que enlistarlos cuando se plantea el archivo de interconexión. Estos nombres nuevamente pueden y es recomendable que sean distintos a los nombres de los puertos en el archivo original. Es importante escribir un cero al final, después de enlistar todos los puertos, para correcta sintaxis de la herramienta.