gerardo alfonso roque romero

92
DESARROLLO DE ARQUITECTURA TIPO RISC PARA SISTEMAS EMBEBIDOS GERARDO ALFONSO ROQUE ROMERO PONTIFICIA UNIVERSIDAD JAVERIANA FACULTAD DE INGENIERÍA CARRERA DE INGENIERÍA ELECTRÓNICA BOGOTÁ, D.C. 2010

Upload: others

Post on 25-Mar-2022

3 views

Category:

Documents


0 download

TRANSCRIPT

DESARROLLO DE ARQUITECTURA TIPO RISC PARA SISTEMAS EMBEBIDOS

GERARDO ALFONSO ROQUE ROMERO

PONTIFICIA UNIVERSIDAD JAVERIANA

FACULTAD DE INGENIERÍA

CARRERA DE INGENIERÍA ELECTRÓNICA

BOGOTÁ, D.C.

2010

2

DESARROLLO DE ARQUITECTURA TIPO RISC PARA SISTEMAS EMBEBIDOS

GERARDO ALFONSO ROQUE ROMERO

Director:

ALEJANDRO FORERO GUZMÁN MSc.

Ingeniero Electrónico

PONTIFICIA UNIVERSIDAD JAVERIANA

FACULTAD DE INGENIERÍA

CARRERA DE INGENIERÍA ELECTRÓNICA

BOGOTÁ, D.C.

2010

Trabajo de grado presentado como

requisito parcial para optar al título

de Ingeniero Electrónico.

3

PONTIFICIA UNIVERSIDAD JAVERIANA

FACULTAD DE INGENIERÍA

CARRERA DE INGENIERÍA ELECTRÓNICA

RECTOR MAGNÍFICO: R.P. JOAQUIN EMILIO SANCHEZ GARCÍA S.J

DECANO ACADÉMICO: Ing. FRANCISCO JAVIER REBOLLEDO MUÑOZ

DECANO DEL MEDIO UNIVERSITARIO: R.P SERGIO BERNAL RESTREPO S.J

DIRECTOR DE CARRERA: Ing. JUAN MANUEL CRUZ BOHÓRQUEZ M, Ed.

DIRECTOR DEL PROYECTO: Ing. ALEJANDRO FORERO GUZMÁN .MSc

4

NOTA DE ADVERTENCIA

“La Universidad no se hace responsable de los conceptos emitidos por algunos de sus alumnos enlos

proyectos de grado. Solo velará porque no se publique nada contrario al dogma y la moral católica y

porque no contengan ataques o polémicas puramente personales. Antes bien, que se vea en ello el

anhelo de buscar la verdad y la justicia.”

Artículo 23 de la Resolución No. 13, del 6 de

julio de 1946, por la cual se reglamenta lo

concerniente a Tesis y Exámenes de Grado en

la Pontificia Universidad Javeriana.

5

AGRADECIMIENTOS

Doy mis agradecimientos a mi familia que siempre estuvo conmigo y me apoyó en todo el proceso,

moral y económicamente. Sin su vital apoyo nada de esto sería posible.

A todos los docentes de la Universidad, a través de los cuales aprendí muchas lecciones, no solo a

nivel académico, sino de vida. Es, en gran parte, gracias a su gran aporte que todo lo que me he

propuesto ha podido lograrse.

Agradezco especialmente al ingeniero Alejandro Forero Guzmán por su apoyo académico, por

permitirme explorar diferentes ideas, siempre con un punto de vista crítico de las cosas. Su

conocimiento ha impulsado gran parte de este documento.

Al grupo de trabajo del laboratorio de electrónica, quienes siempre nos brindaron el mejor servicio.

DEDICATORIAS

Dedico este trabajo de grado a mis padres y mi hermana por su apoyo incondicional y sus consejos

en los momentos más difíciles. A mi pareja, quien ha sido el recurso más valioso que he podido

encontrar en los últimos meses. Su gran aliento ha permitido que todo esto haya sido cumplido y

valorado. También a mis amigos y compañeros de carrera, que han influido en mí de formas que tal

vez no esperaban, y me han hecho crecer como persona. Espero para ellos lo mejor, de mí siempre lo

tendrán.

GERARDO ALFONSO ROQUE ROMERO

6

Tabla de Contenido

1. INTRODUCCIÓN ........................................................................................................................ 8 2. MARCO TEÓRICO ..................................................................................................................... 8 3. ESPECIFICACIONES ................................................................................................................ 10 4. DESARROLLOS ....................................................................................................................... 11

4.1. Diagrama de Bloques General ..................................................................................................... 11 4.1.1. Unidad de Memoria. ................................................................................................................................ 11 4.1.2. Núcleo. .................................................................................................................................................... 12 4.1.3. Bloques de Lógica. .................................................................................................................................. 13 4.1.4. Registros. ................................................................................................................................................. 15

4.2. Conjunto de Instrucciones ............................................................................................................ 16 4.2.1. Formato de Instrucciones......................................................................................................................... 16 4.2.2. Código de Operación y Tipo de Operación ............................................................................................. 18 4.2.3. Conjunto de Instrucciones ....................................................................................................................... 18

5. PRUEBAS DE SIMULACIÓN E IMPLEMENTACIÓN ................................................................. 39 5.1. Simulaciones para el Núcleo ........................................................................................................ 39

5.1.1. Simulación 1 ............................................................................................................................................ 40 5.1.2. Simulación 2 ............................................................................................................................................ 40 5.1.3. Simulación 3 ............................................................................................................................................ 40 5.1.4. Simulacion 4 ............................................................................................................................................ 40

5.2. Simulaciones para el Sistema de Prueba ...................................................................................... 40 5.3. Implementación y Ejecución del Sistema de Prueba en FPGA .................................................... 42

6. ANÁLISIS DE RESULTADOS .................................................................................................... 43 6.1. Desempeño Actual del Sistema ..................................................................................................... 43 6.2. Mejoras en la Unidad de Datos .................................................................................................... 44 6.3. Mejoras en la Unidad de Control. ................................................................................................ 45

7. CONCLUSIONES ...................................................................................................................... 46 8. BIBLIOGRAFÍA ........................................................................................................................ 47 9. ANEXOS................................................................................................................................... 48

9.1. Anexo: ALU .................................................................................................................................. 48 9.1.1. U_ARIT ................................................................................................................................................... 49 9.1.2. U_AND, U_OR, U_NOR ........................................................................................................................ 52 9.1.3. LOGICA_OVERFLOW .......................................................................................................................... 52 9.1.4. SEL_COMPARACION .......................................................................................................................... 53 9.1.5. LOGICA_COMPARACION_CERO ...................................................................................................... 53 9.1.6. ALU_SEL_OUT ..................................................................................................................................... 53

9.2. Anexo: Registros Internos de Propósito General ......................................................................... 54 9.2.1. Estructura de Entrada de Datos................................................................................................................ 54 9.2.2. Estructura de Control ............................................................................................................................... 56 9.2.3. Estructura de Salida de Datos .................................................................................................................. 58

9.3. Anexo: Diagramas de Tiempos ..................................................................................................... 59 9.3.1. Diagrama 1: Operaciones Aritméticas y Lógicas .................................................................................... 59 9.3.2. Diagrama 2: Comparaciones ................................................................................................................... 60 9.3.3. Diagrama 3: Ramificaciones ................................................................................................................... 60 9.3.4. Diagrama 4: Carga de Datos desde Memoria .......................................................................................... 61 9.3.5. Diagrama 5: Almacenamiento de Datos en Memoria .............................................................................. 62 9.3.6. Diagrama 6: Carga de un Dato Inmediato ............................................................................................... 63 9.3.7. Diagrama 7: Habilitación e Inhabilitación de Interrupciones .................................................................. 63 9.3.8. Diagrama 8: Retorno desde Rutina de Interrupción ................................................................................. 64 9.3.9. Diagrama 9: Atención a Solicitud de Interrupción .................................................................................. 64 9.3.10. Diagrama 10: Excepciones ................................................................................................................. 65 9.3.11. Diagrama 11: Reintento de Búsqueda de Instrucción por Código de Operación Ilegal ...................... 66 9.3.12. Diagrama 12: Fetch16 ........................................................................................................................ 67 9.3.13. Diagrama 13: Fetch32 ........................................................................................................................ 68

9.4. Anexo: Máquina de Estados De Unidad de Control .................................................................... 69 9.5. Anexo: Protocolo de Pruebas ....................................................................................................... 78

9.5.1. Esquema del Protocolo ............................................................................................................................ 78

7

9.5.2. Codigo Fuente de Programa de Prueba.................................................................................................... 83 9.6. Anexo: Simulaciones ..................................................................................................................... 83

9.6.1. Núcleo ..................................................................................................................................................... 83 9.6.2. Sistema .................................................................................................................................................... 86

9.7. Anexo: Prueba en FPGA del Sistema ........................................................................................... 88 9.8. Anexo: Código VHDL del Sistema, Distribución del Directorio de VHDL en el Disco Adjunto . 90

8

1. Introducción

En la actualidad, la Facultad de Ingeniería, Departamento de Electrónica, cuenta con el resultado de un

proceso de desarrollo por parte de la Sección de Técnicas Digitales, el cual es un proyecto que consiste

en la arquitectura de un procesador, llamado BINARIC, enfocada al procesamiento y aplicaciones de

propósito general. Por esta razón, es complejo para desarrollar aplicaciones de proyectos de propósito

específico.

Se desarrollará una arquitectura que sea ideada bajo unos criterios completamente diferentes a los de

su antecedente: un núcleo tipo RISC, compacto y sencillo, con enfoque en procesamiento y

aplicaciones de propósito específico. La simplicidad detrás de la arquitectura debe permitir su

implementación en hardware a velocidades de reloj altas. También, debe permitir su completa

integración en un sistema digital embebido más complejo, en los cuales puede manejarse memoria y

periféricos embebidos.

El núcleo debe ser capaz de realizar todas las operaciones de manera eficiente y veloz, haciendo uso

de los recursos del sistema de la mejor manera posible. Este tipo de diseño estará en capacidad de ser

utilizado en la implementación de diferentes configuraciones de sistemas de propósito específico. Esto

es deseable, ya que un único núcleo puede ser la base para la creación de sistemas digitales (sistemas

embebidos con diferentes tipos de periféricos, o con más de un núcleo) que se pueden usar para la

resolución de diferentes problemas en áreas tanto académicas e investigativas, como posiblemente

comerciales.

Por estas razones la arquitectura debe ser simple y escalable. Debe permitir flexibilidad en cuanto a su

interconexión con diferentes sistemas digitales embebidos, y en cuanto a la capacidad que tenga para

la resolución de problemas específicos.

2. Marco Teórico

En lo considerado como arquitecturas convencionales, hoy en día, existen, básicamente, dos divisiones

generales. Por un lado se encuentran las arquitecturas tipo CISC (Complex Instruction-Set Computer),

cuyo diseño se desarrolló en medio de unas condiciones tecnológicas mucho más limitadas que lo que

se encuentra actualmente disponible. El costo de grandes cantidades de memoria, su baja velocidad, la

inexistencia de compiladores, y el costo de implementación de registros llevaron a la industria a

construir arquitecturas con conjuntos de instrucciones complejos, con instrucciones altamente

codificadas, encargadas de muchas labores, y de longitud variable. Se implementaban instrucciones

que hicieran varias tareas a la vez, y que fueran muy robustas por sí mismas. Esto reducía el tiempo

necesario para escribir un programa, bien fuera en código de máquina o en lenguaje ensamblador. Así,

se cambiaba tiempo de codificación de programa por tiempo de ejecución de instrucciones. Sumado a

esto, los procesadores CISC tenían pocos, o 1 solo, registros de propósito general. Así, se diseñaban

instrucciones que operaran sobre la memoria directamente. En cuanto a los modos de

direccionamiento, siempre se intentaba que el Conjunto de Instrucciones fuera lo más ortogonal

posible, con el fin de que, si una instrucción cambiaba en una futura revisión de un dispositivo, no

fuera a afectar al resto del sistema en cuanto al acceso a memoria.[4][5]

A medida que estas condiciones empezaron a cambiar, la industria empezó a replantear muchas de las

normativas generales en las cuales se basaba el diseño de arquitecturas. Liderando esta tendencia, dos

universidades muy importantes de los Estados Unidos presentaron dos propuestas diferentes. Estas

universidades son Stanford y Berkeley. Los proyectos que estas dos instituciones desarrollaron serían

el punto de partida para el cambio de paradigma que ocurrió respecto al desarrollo de

arquitecturas.[1][3][4][5]

9

La universidad de Berkeley llevó a cabo una serie desarrollos y estudios a comienzos de la década de

1980, basándose en la idea de que una escogencia apropiada de un conjunto pequeño de instrucciones,

con modos de direccionamiento limitados, y un adecuado planeamiento de la distribución espacial de

los transistores dentro del chip, podían llevar a la obtención de una arquitectura con alto desempeño.

Además del hardware, el usuario de lenguajes de alto nivel debía poder tener una máquina eficiente.

Así, no solo el hardware debía cambiar, sino también el funcionamiento de los compiladores, pues era

necesario que los comandos del lenguaje de alto nivel fueran traducidos a instrucciones que se

ejecutaran de manera eficiente, y teniendo en cuenta el tamaño del conjunto de instrucciones de la

máquina. Es así como se llega a 2 prototipos, el RISC I, y el RISC II, fabricados en 1981 y 1982

respectivamente.[1]

Al tiempo que la Universidad de Berkeley se dedicaba al desarrollo de sus prototipos, Stanford

también lo estaba haciendo. En el año 1981 se estaba trabajando en el desarrollo del primer procesador

MIPS (“Microprocessor without Interlocked Pipeline Stages”). El concepto más importante detrás de

este procesador era la optimización de ejecución de instrucciones por medio de “pipelining”[4][10],

método en el cual se cargan varias instrucciones en el procesador a la vez, y éste va ejecutándolas de

forma sobrelapada. Además de esto, también se quería que todas las instrucciones se pudieran ejecutar

en un único ciclo de máquina, requerimiento bastante difícil de completar, hasta el punto en que el

primer diseño carecía de instrucciones de multiplicación y división, pues no cumplían el requisito.

Este mismo requisito era el que le daba el nombre de MIPS, pues no era necesario detener ninguna

instancia del “pipeline” a la espera de la ejecución de una instrucción compleja. Otras características

del procesador eran una gran cantidad de registros internos, y pocos modos de direccionamiento, todas

afines con la filosofía RISC.[3][5]

Posteriormente, muchas más empresas desarrolladoras de hardware surgieron, basadas en los

conceptos que traía la filosofía de diseño RISC, impuesta por los dos proyectos antes mencionados.

Así, surgirían arquitecturas RISC de todo tipo y para todo tipo de aplicaciones, desde

supercomputadores y servidores, hasta sistemas embebidos.[2][4][5][6][7]

Bajo el nuevo paradigma RISC se encuentran prácticas de diseño diametralmente opuestas a lo que se

venía haciendo en las arquitecturas CISC: conjuntos de instrucciones con instrucciones que ejecutan

tareas mucho más sencillas, muchos registros internos, y simplificación general de la codificación de

las instrucciones, todas de longitud uniforme. Este tipo de arquitectura permite implementar

optimizaciones sobre las instrucciones que más comúnmente se ejecutan, haciendo que lo hagan en

uno, o unos pocos, ciclos de reloj. Con instrucciones sencillas, se reduce el tiempo de decodificación

de las mismas, haciendo que sea posible tener casos en los cuales una tarea sea ejecutada más

rápidamente por medio de varias instrucciones tipo RISC que con una sola instrucción tipo CISC.

También, la ventaja de tener más registros internos es innegable, pues hacer operaciones sobre el

mismo núcleo es más veloz que hacer operaciones sobre la memoria. Así, el costo de acceder varias

veces a memoria para buscar los operandos de una operación, se justifica por la velocidad de ejecución

de la operación sobre los operandos dentro del procesador (por dar un breve ejemplo). En

consecuencia, las instrucciones de una arquitectura RISC solo operan datos que se encuentren en los

registros internos, mas no directamente en memoria. Por esta misma razón se conoce también como

arquitectura tipo Load-Store, pues solo se accede la memoria para cargar datos desde ella, o para

almacenarlos en ella.[4][5]

Actualmente, la línea que divide las arquitecturas CISC y RISC se está desvaneciendo levemente,

hasta al punto en que arquitecturas modernas tipo CISC se implementan internamente por medio de un

núcleo RISC que convierte cada instrucción CISC en varios pasos tipo RISC. Muchos de los

conceptos RISC se han aplicado con éxito en arquitecturas CISC, y arquitecturas que podrían

considerarse híbridas, por combinar conceptos de ambas filosofías, son lo más común. Así, es posible

hablar de un núcleo que es RISC en algún sentido, más no completamente.[4][5]

10

3. Especificaciones

La arquitectura que se diseña es la de un núcleo tipo RISC, de propósito específico, para su

implementación en sistemas embebidos. Dado el tipo de aplicación a la cual se enfoca, no es un núcleo

complejo. Es, por el contrario, un núcleo sencillo, que debe ser a su vez veloz y versátil.

Es un procesador con 16 registros internos de propósito general de 16 Bits, accesibles al programador.

El núcleo está en capacidad de realizar operaciones aritméticas y lógicas. Dentro de las operaciones

aritméticas, es capaz de realizar sumas y restas, con o sin signo, con datos representados en

complemento a 2. Las operaciones lógicas que realiza son comparaciones entre datos con signo y sin

signo, NOR, AND y OR. Además de esto, está en capacidad de hacer saltos en el flujo de programa,

permitiendo condicionar ejecución de secciones de programas, o de hacer bucles. Es capaz de cargar

datos desde memoria de 16 Bits.

Es capaz de manejar tres diferentes fuentes de interrupción, sin anidamiento. Es decir, cuando se está

atendiendo una interrupción, no es posible atender más interrupciones dentro de la misma, hasta que

ésta no termine. La escogencia de este modo de operación tiene que ver únicamente con cuestiones de

restricción de tiempo y de complejidad del diseño: estructurar un sistema de interrupciones anidadas,

por medio del uso de una pila en memoria y un apuntador interno a dicha pila, le da mucho mejor

tiempo de respuesta al sistema que de la manera como se ha implementado actualmente. Sin embargo,

en vista del enfoque principal del proyecto, cuyos fines son, tanto parte de un diseño compacto como

de un ejercicio académico, es necesario limitar la funcionalidad del mismo a algo que sea realizable y

depurable de manera correcta dentro del plazo de tiempo establecido para su terminación.

Tiene una unidad de control que reconoce 18 diferentes instrucciones, además de tener secuencia de

inicio, atención a interrupciones y atención a excepciones causadas por errores en la operación del

sistema. Todas las instrucciones son de un largo de 16 o 32 Bits, dependiendo de la instrucción. Esto

se realiza con el fin de evitar la carga de una segunda palabra para una instrucción que, por la cantidad

de operandos, no lo requiera. El código de operación de todas las instrucciones ocupa 5 Bits de ancho,

sin embargo existe espacio suficiente en todas las instrucciones para ampliarlo, en caso de futuras

implementaciones de nuevas instrucciones o de ampliación de la arquitectura. Teniendo en cuenta que

el núcleo se diseña con conceptos RISC, la unidad de control debe ser preferiblemente segmentada.

Por cuestiones de tiempo, no es posible lograr esto. Sin embargo, las instrucciones deben estar en

capacidad de adecuarse lo más óptimamente posible a una unidad de control segmentada. Para lograr

esto, es importante que todas las instrucciones se ejecuten en tiempos aproximadamente iguales. Las

instrucciones que más tiempo toman en ejecutarse se demoran cuatro eventos de reloj, la gran mayoría

se demora tres eventos, y unas pocas tardan dos eventos. Para este caso, el balanceo de instrucciones

solo requiere alargar todas las instrucciones a 4 eventos de reloj. Si bien esto disminuye la velocidad

de ejecución de las instrucciones, el mejoramiento a nivel de ejecución de instrucciones por segundo

mejorará, en vista de que este procedimiento se hará en conjunto con la segmentación de la unidad de

control. En vista del enfoque del núcleo, instrucciones complicadas tales como multiplicaciones y

divisiones no han sido implementadas.

La estructura de memoria es tipo Von Neumann. El direccionamiento a la memoria se hace por medio

de los registros internos de 16 Bits. Esto implica que la memoria tiene un tamaño máximo de 2^16

espacios. Todos estos espacios son de 16 Bits de ancho, con el fin de agilizar el tiempo de

almacenamiento y carga de datos e instrucciones desde y hacia el núcleo. Ya que cada instrucción es

de 16 o 32 Bits de ancho, esto significa que cada instrucción ocupa un total de 1 o 2 espacios

contiguos de memoria por instrucción. Las instrucciones no están alineadas, lo que significa que las

instrucciones pueden empezar en cualquier dirección de memoria. La memoria es de carácter veloz y

síncrona, reaccionando, tanto para lectura como escritura, con el borde de subida de la señal de reloj

del sistema. La lectura se realiza en un ciclo de reloj, al igual que la escritura.

El núcleo se espera que pueda ejecutar programas a una velocidad de reloj no inferior a 50MHz. Para

lograr esto, se ha reducido la cantidad de lógica combinatoria a lo mínimo necesario. Además, el

11

sumador de la ALU implementa un sistema de acarreo predictivo para grupos de 4 Bits, lo que permite

la aceleración del cálculo de operaciones aritméticas.

Para implementar físicamente el sistema, se ha escogido el chip FPGA Stratix II, de Altera. Se ha

escogido este chip con el fin de tener espacio suficiente dentro del chip para que todo el sistema quepa

sin tener que hacer concesiones de recursos, tales como el uso completo de los caminos rápidos de

intercomunicación entre macroceldas. También, es el chip adecuado para implementar la memoria

interna en su totalidad (64 KBytes de memoria de 16 Bits de ancho). Además, es un chip que ofrece

velocidades de reloj superiores a 100 MHz, de acuerdo a su hoja de especificaciones, aún para

proyectos con alto uso de recursos[12]. La forma en que se trabajará será por medio de las

herramientas incluidas en el kit de desarrollo de Altera llamado “NIOS II Development Kit, Stratix II

Edition”, del cual puede encontrarse información detallada en la referencia [13].

Para el desarrollo de la descripción en hardware del sistema sobre el chip FPGA escogido, se utilizará

la herramienta de desarrollo de software Quartus II que viene con el kit de desarrollo mencionado. Es

la herramienta más apropiada para trabajar en conjunto con el chip FPGA escogido, pues la compañía

que produce el chip también desarrolla esta herramienta. Esto asegura el aprovechamiento más óptimo

de los recursos del chip FPGA que será utilizado.

4. Desarrollos

El núcleo está compuesto por una serie de sistemas digitales que se interconectan entre sí para lograr

una funcionalidad completa. Es necesario describir todo el sistema, desde un punto de vista general,

para tener una idea de qué es lo que el núcleo es capaz de realizar.

Figura 1. Diagrama de bloques general del sistema.

4.1. Diagrama de Bloques General

El sistema consiste del núcleo, y una unidad de memoria. Esta unidad de memoria tiene la

característica de ser veloz y síncrona, y por tanto no requiere señal de espera de para el núcleo, pues se

asume que responde en un tiempo inferior al que el núcleo espera para usar un dato leído de ella, o

escribir un dato en ella. El tiempo de respuesta de la memoria es menor a medio ciclo de reloj.

4.1.1. Unidad de Memoria.

Se asume que la unidad de memoria contiene tres señales que permiten su control. Estas señales son

ENABLE, READ_ENA, WRITE_ENA, y están representadas por la señal C en la Figura 1. Por medio

de las tres señales se indica a la Unidad de Memoria que se requiere acceso a ella, y además le indica

qué operación debe ejecutarse: lectura o escritura de información.[9] El bus de datos es de 16 Bits, al

igual que el bus de direcciones, lo cual significa que se tiene 64KBytes (1 Byte son 8 Bits para todos

los casos en los que se refiera a esta medida de capacidad) de memoria de 16 Bits disponibles para

instrucciones y datos [8][9]. Por medio del bus de direcciones, se indica a la Unidad de Memoria a qué

posición se requiere acceso, bien sea para lectura o escritura de datos. Este es un bus unidireccional.

Para la escritura de datos se usa el bus BUS_WRITE, y para la lectura de datos se usa el bus

12

BUS_READ. Ambos buses son unidireccionales, de 16 Bits de ancho. Por medio de estos dos buses,

el núcleo y la Unidad de Memoria transfieren información entre sí.

El sistema tiene registros internos de propósito general de 16 Bits. Por esta razón, lo más apropiado es

utilizar una memoria de 16 Bits por posición. Esto permite fácil organización de los datos en memoria.

Para casi todas las operaciones del sistema con la Unidad de Memoria solo se requiere un acceso a

esta. El único caso en el que se requiere acceder a información dos veces en una misma operación es

para la búsqueda de instrucciones, pues estas son de 16 o 32 Bits de ancho.

4.1.2. Núcleo.

Dentro del núcleo se tienen todos los bloques que componen al sistema. El diagrama se muestra a

continuación.

Figura 2. Diagrama de Bloques del núcleo.

En el sistema se muestran 13 bloques diferentes. Todos estos bloques componen al Núcleo. Para cada

bloque existen señales de control, demarcadas en color rojo. Estas señales provienen de la Unidad de

Control, bloque U_CONTROL, pero no se conectan explícitamente a éste para facilitar la lectura del

diagrama. Además, hay señales que van a la Unidad de Control, también de color rojo. Todas las

señales de control se encargan de activar los bloques de forma correcta, de acuerdo a lo que cada

instrucción o acción del núcleo requiera. La señal MASTER_RESET es de 1 Bit, y es externa al

sistema. Cuando su valor es „1‟ lógico, se reinicia la Unidad de Control en el primer paso, además de

reiniciarse el registro SR. Para ver información detallada sobre el funcionamiento lógico de la Unidad

de Control, se puede ver la sección de Anexos 9.4.

13

El sistema carece de un Stack Pointer dedicado para el manejo de una pila. Esto limita la respuesta del

sistema en lo referente a interrupciones. La forma como se manejan las interrupciones es por medio

del almacenamiento de la dirección almacenada en el Program Counter y el contexto del Stack

Pointer en registros con el mismo tamaño que estos, internos al sistema. De esta manera, es posible

mostrar la funcionalidad de las interrupciones, a la vez que se realiza de una forma sencilla y directa.

En una implementación más robusta, se requiere de un registro adicional que apunte a un lugar

específico en memoria durante el inicio del sistema, y que sea usado para almacenar el contexto del

sistema y la dirección de la instrucción a ejecutar posteriormente. Además, el mecanismo de

interrupción debería controlar el almacenamiento y carga de esta información, desde y hacia memoria,

y hacia y desde los registros apropiados.

Como se explicó anteriormente, se implementó un sistema de manejo de interrupciones y excepciones

más sencillo que el descrito en el diseño por cuestiones de tiempo, y para mantener la simplicidad del

diseño, sin quitar al toda la funcionalidad de las interrupciones. Esta es, por tanto, una sección del

núcleo que es expandible para entregarle mayor funcionalidad al mismo. Lo que se requiere es la

adición de un registro que funcione como apuntador a memoria permanentemente, la eliminación de

los registros internos que almacenan el contexto y la dirección de ejecución de la siguiente instrucción,

y por último, la adecuación de las acciones realizadas por el núcleo durante la atención a

interrupciones y excepciones, y el retorno desde estas hacia el flujo normal de programa.

4.1.3. Bloques de Lógica.

4.1.3.1. ALU.

Se encarga de realizar operaciones aritméticas, lógicas y de comparación entre dos operandos. Se

pueden realizar sumas y restas, con datos interpretados como con y sin signo, comparaciones y

operaciones lógicas AND, OR y NOR. Tiene dos señales de entrada de datos: OP1 de 16 Bits, el

primer operando; OP2 de 16 Bits, el segundo operando. Tiene cuatro señales de salida de datos: N de 1

Bit, señal que indica cuando una comparación da negativa o positiva; Z de 1 Bit, que indica cuando

ambos operandos son iguales; ALU_OUT de 16 Bits, que lleva el resultado de la operación

aritmética/lógica que se haya realizado entre las entradas; V de 1 Bit, indica cuando hay un overflow

aritmético a la Unidad de Control. Para más información se puede revisar la sección de Anexo 9.1.

4.1.3.2. OPCODE_DET.

Se encarga de determinar si es necesario realizar la búsqueda de 32 Bits para una instrucción, o solo de

16 Bits. Ya que no todas las instrucciones requieren más de 16 Bits para indicar al núcleo su

comportamiento completo, no es necesario traer los otros 16 Bits, pues no se usarán. Este bloque

determina eso, y mediante su señal de salida indica a la Unidad de Control si debe traer el otro pedazo

de la instrucción o si no se requiere. Tiene una señal de entrada de datos, R_INST_OUT[30] de 1 Bit,

correspondiente al cuarto Bit más significativo del código de operación de la instrucción. Tiene una

señal de salida que va a la Unidad de Control, OPCODE_DET_OUT, de 1 Bit. Por la forma como

están distribuidos los códigos de operación entre todas las instrucciones, para aquellas que usan los 32

Bits, siempre R_INST_OUT[30] = „0‟, y por tanto, la ecuación lógica para este bloque es

OPCODE_DET_OUT = R_INST_OUT[30]‟

4.1.3.3. ILEGAL_OP_DET.

Se encarga de determinar si el código de operación que se está cargando en la instrucción es válido o

no. En caso de ser válido, su valor de salida es 0. Si es inválido, su valor de salida es 1. Tiene una

señal de entrada de datos, BUS_READ[15:11] de 5 Bits, correspondiente a los 5 Bits más

significativos de la instrucción, posición en la que se encuentra el OpCode de la misma. Tiene una

señal de salida que va a la Unidad de Control, ILEGAL_OP_DET_OUT, de 1 Bit. Cuando se intenta

ejecutar una instrucción no válida, la Unidad de Control activa un Bit del registro SR, indicando que

ha habido un error interno, y vuelve a intentar leer la misma instrucción. Si la instrucción es errónea de

14

nuevo, y además ya hubo una lectura previa por error de código ilegal, se genera una excepción. A

continuación se muestra la tabla de verdad que representa la operación de este bloque.

R_INST_OUT[31] R_INST_OUT[30] R_INST_OUT[29] R_INST_OUT[28] R_INST_OUT[27] ILEGAL_OP_DET_OUT

0 0 0 0 0 0

0 0 0 0 1 0

0 0 0 1 0 0

0 0 0 1 1 0

0 0 1 0 0 0

0 0 1 0 1 0

0 0 1 1 0 0

0 0 1 1 1 1

0 1 0 0 0 0

0 1 0 0 1 0

0 1 0 1 0 0

0 1 0 1 1 1

0 1 1 0 0 0

0 1 1 0 1 0

0 1 1 1 0 0

0 1 1 1 1 1

1 0 0 0 0 0

1 0 0 0 1 0

1 0 0 1 0 1

1 0 0 1 1 1

1 0 1 0 0 1

1 0 1 0 1 1

1 0 1 1 0 0

1 0 1 1 1 1

1 1 0 0 0 0

1 1 0 0 1 1

1 1 0 1 0 1

1 1 0 1 1 1

1 1 1 0 0 1

1 1 1 0 1 1

1 1 1 1 0 1

1 1 1 1 1 0

Tabla 1. Tabla de verdad que representa el circuito de lógica combinatoria ILEGAL_OP_DET.

A partir de la Tabla 1, y utilizando mapas de Karnaugh, es posible reducir la tabla a la siguiente

ecuación lógica:

ILEGAL_OP_DET_OUT = D^C‟^B^A + E^D^C‟^A + E‟^C^B^A + E^D^C^A‟ + E^D‟^C^A +

E^C‟^B + E^C^B‟

Donde:

E=BUS_READ[15], D=BUS_READ[14], C=BUS_READ[13], B=BUS_READ[12],

A=BUS_READ[11].

4.1.3.4. MUX_SR_IN.

Multiplexor 2:1, de 5 Bits de ancho, que se encarga de escoger qué señales se almacenan en el registro

SR. Tiene dos señales de entrada de datos: [IA_IN:CT_IN:IE_IN:ALU_SR_IN] de 5 Bits;

SR_C_OUT de 5 Bits. Tiene una señal de salida de datos, SR_IN, de 5 Bits. Tiene una señal de

entrada de control, MUX_SR_IN_C, de 1 Bit, que escoge como señal de salida de datos entre la

primera y la segunda entrada de datos.

4.1.3.5. MUX_R_BANK_IN.

Multiplexor 4:1, de 16 Bits de ancho, que se encarga de escoger qué señal se almacen en el algún

registro del bloque R_BANK. Tiene tres señales de entrada de datos: R_ALU_OUT de 16 Bits;

R_INST_OUT de 16 Bits; BUS_READ de 16 Bits. Tiene una señal de salida de datos, BUS_IN, de 16

Bits. Tiene una señal de entrada de control, MUX_R_BANK_IN_C, de 2 Bit, que escoge como señal

de salida de datos entre las tres entradas de datos.

4.1.3.6. MUX_BUS_DIR_IN.

15

Multiplexor 4:1, de 16 Bits de ancho, que se encarga de escoger qué señal se utiliza como dirección

para la Unidad de Memoria. Tiene cuatro señales de entrada de datos: PC_OUT de 16 Bits;

R_ALU_OUT de 16 Bits; R_IRQ_OUT de 16 Bits; el valor binario fijo 0x0000. Tiene una señal de

salida de datos, BUS_DIR, de 16 Bits. Tiene una señal de entrada de control, MUX_BUS_DIR_IN_C,

de 2 Bit, que escoge como señal de salida de datos entre las cuatro entradas de datos.

4.1.4. Registros.

4.1.4.1. R_BANK.

Representa los 16 registros internos de propósito general del sistema. Todos los registros son de 16

Bits de ancho. Dentro de este banco de registros se tienen 2 registros especiales. Uno de los registros

entrega siempre el valor 0x0000 a su salida, y el otro registro es el Program Counter del sistema.

Tiene cuatro señales de entrada de datos: BUS_IN de 16 Bits; R_INST[3:0] de 4 Bits; R_INST[19:16]

de 4 Bits; R_INST[23:20] de 4 Bits. Tiene tres señales de salida de datos: OP1 de 16 Bits; OP2 de 16

Bits; PC_OUT de 16 Bits. Tiene seis señales de control: MUX_DEC_C de 1 Bits; MUX_PC_C de 3

Bits; PC_C_C de 1 Bit; OP1_SOURCE_C de 1 Bits; W_ENA de 1 Bit; CLK de 1 Bit, el reloj del

sistema. Para más información puede revisarse la sección de Anexos 9.2.

4.1.4.2. R_INST.

Se encarga de recibir y almacenar la instrucción de memoria que se encuentra en la posición indicada

por el Program Counter, para su inmediata decodificación por parte de la Unidad de Control. Sin

embargo, como las instrucciones son de 32 Bits de ancho, y el Bus de Datos es de 16 Bits, es necesario

traer los 4 Bytes de cada instrucción, dos Bytes a la vez. Algunos de estos Bits van a la Unidad de

Control, y otros al bloque R_BANK. Dado que uno de los datos que contiene la instrucción puede ser

un dato inmediato de 16 Bits, es necesario poder sacarlo de la instrucción para enviarlo al registro

interno deseado, y por esto se conectan los Bits R_INST_OUT[19:4] a una de las entradas del bloque

MUX_R_BANK_IN. No todas las instrucciones son de 32 Bits, pues no todas las instrucciones

utilizan más de 16 Bits. Por esto, siempre se trae primero los 16 Bits que contienen el Código de

Operación de la instrucción, para poder determinar si es necesario traer los otros 16 Bits (esto lo

determina el bloque de lógica OPCODE_DET).Tiene una señal de entrada de datos: BUS_READ de

16 Bits, conectada tanto 16 Bits menos significativos como a los 16 Bits más significativos. Tiene una

señal de salida de datos: R_INST_OUT, de 32 Bits. Tiene tres señales de control: R_INST_C_L de 1

Bit, controla la escritura de los 16 Bits menos significativos del registro; R_INST_C_H de 1 Bit,

controla la escritura de los 16 Bits más significativos del registro; CLK de 1 Bit, el reloj del sistema.

4.1.4.3. SR.

También es conocido como Status Register por el papel que cumple. Este registro es de 5 Bits de

ancho, y se encarga de almacenar el estado de diferentes operaciones aritméticas y lógicas que el

núcleo debe realizar. Todos sus Bits tienen nombres: N, Z, IE, CT, IA, correspondientes a SR[0],

SR[1], SR[2], SR[3] y SR[4], respectivamente. N está activo si el resultado de una comparación entre

dos datos genera como resultado que el segundo operando es mayor al primero; Z está activo si un

resultado aritmético es cero; IE es el Bit que indica si las interrupciones están habilitadas o no, para el

núcleo, a nivel general; CT se activa cuando existe un error por ejecución de código de operación

indefinido por primera vez, y se desactiva siempre que se ejecute un código de operación definido; IA

se activa durante una atención a una interrupción o excepción, y se desactiva cuando no se está

atendiendo una, esto con el fin de poder generar una excepción en caso de que el usuario erróneamente

desee salir de una interrupción sin estar dentro de una. Todas las señales de salida van a la Unidad de

Control, con el fin de que ésta pueda revisar los resultados de las operaciones aritméticas y lógicas que

ocurren en la ALU, y ejecutar ramificaciones de acuerdo a esto, además de revisar condiciones

excepcionales. Tiene una señal de entrada de datos: SR_IN de 5 Bits[3]. Tiene una señal de salida de

datos: SR_OUT de 5 Bits. Tiene ocho señales de control: SR_N_C de 1 Bit, controla la escritura del

Bit N del registro; SR_Z_C de 1 Bit, controla la escritura del Bit C del registro; SR_IE_C de 1 Bit,

controla la escritura del Bit IE del registro; SR_CT_C de 1 Bit, controla la escritura del Bit CT del

16

registro; SR_IA_C de 1 Bit, controla la escritura del Bit IA del registro; MASTER_RESET de 1 Bit,

se encarga de reiniciar el valor del registro a „00000‟ cuando el sistema está inicializando; SR_RST,

de 1 Bit, se encarga de reiniciar los primeros cuatro Bits del registro en el valor lógico „0‟ cuando se

empieza la atención a una excepción o interrupción; CLK de 1 Bit, el reloj del sistema.[5]

4.1.4.4. SR_C.

Registro de cinco Bits. Todos sus Bits corresponden a los Bits del bloque SR. Esto es necesario, pues

en caso de que exista una petición para atender una interrupción, o una excepción, el valor de SR se

almacenará en SR_C. Así, todas las salidas de SR son a su vez las entradas de SR_C. Las salidas de

SR_C están conectadas al bloque SR mediante el multiplexor MUX_SR_IN. Tiene una señal de

entrada de datos: SR_OUT de 5 Bits. Tiene una señal de salida de datos: SR_C_OUT de 5 Bits. Tiene

dos señales de control: SR_C_C de 1 Bit, controla la escritura del registro; CLK de 1 Bit, el reloj del

sistema.[5]

4.1.4.5. R_ALU.

Registro de 16 Bits de ancho, que se encarga de almacenar la última operación aritmética/lógica que el

bloque ALU realice. Tiene una señal de entrada de datos: ALU_OUT de 16 Bits. Tiene una señal de

salida de datos: R_ALU_OUT de 16 Bits. Tiene dos señales de control: R_ALU_C de 1 Bit, controla

la escritura del registro; CLK de 1 Bit, el reloj del sistema.

4.1.4.6. R_IRQ.

Registro de 16 Bits de ancho, que contiene la dirección del vector de interrupción que se desea obtener

para atender una interrupción. Este registro tiene la siguiente configuración: R_IRQ[1:0] corresponde

a la entrada, IRQ de 2 Bits , lo que permite el cambio de la dirección del vector de interrupción;

R_IRQ[15:2] están siempre en „0‟, lo que hace que las direcciones de los vectores de interrupción

estén en las direcciones 0x0001, 0x0002 y 0x0003. Este registro actúa como registro de indirección

para rutinas de interrupción, por medio del almacenamiento del vector de interrupción de la rutina que

corresponde al periférico que requiere atención. Tiene una señal de entrada de datos: IRQ de 2 Bits,

contiene un valor entre 0 y 3, en binario. Tiene una señal de salida de datos: R_IRQ_OUT de 16 Bits.

Tiene dos señales de control: R_IRQ_C de 1 Bit, controla la escritura del registro; CLK de 1 Bit, el

reloj del sistema.

4.2. Conjunto de Instrucciones

El núcleo es capaz de realizar una determinada cantidad de operaciones. Estas se llaman instrucciones.

Cada instrucción tiene un determinado formato, que en general consiste en un código de operación,

también llamado OpCode[5][8][10], y los operandos que son objeto de dicha operación. No existe un

único formato para todas las instrucciones, pues dependiendo del tipo de acción, la cantidad y tipo de

operandos cambian.

4.2.1. Formato de Instrucciones

En total, se tienen 18 instrucciones diferentes. Entre estas 18 instrucciones se tiene un total de cinco

diferentes formatos. Existen dos tamaños de instrucción, dependiendo de la cantidad de operandos que

se requieren: instrucciones de 16 Bits y de 32 Bits[5]. Es requerido usar instrucciones de 32 Bits para

poder utilizar instrucciones en formato 3-Address Machine. [5][10]

El código de operación es de 5 Bits para todas las instrucciones, lo que permite que la decodificación

de la instrucción sea más sencilla de implementar. Con solo una excepción, todos los operandos de las

instrucciones son de 4 Bits de tamaño. Esto se debe a que se desea poder direccionar 16 diferentes

registros internos, y para lograrlo es necesaria esa cantidad de Bits para determinar un único registro

de entre todo el conjunto[10]. A continuación se muestra cada uno de los formatos, y se explica con

detalle.

17

4.2.1.1. Formato Tipo A.

Figura 3. Formato de instrucción tipo A, dos operandos y un destino.

El formato de instrucción tipo A se muestra en la Figura 3. Su tamaño es de 32 Bits. Contiene el

código de operación en los cinco Bits más significativos. Tiene dos operandos y un destino.

Operando1 y Operando2 son dos registros internos de propósito general, que corresponden a los

argumentos de entrada de la instrucción. Destino es otro registro interno de propósito general, que

corresponde al argumento de salida de la instrucción. Existen 15 Bits que no se utilizan, y que el

sistema ignora. Así, estos pueden tener cualquier información.

4.2.1.2. Formato Tipo B.

Figura 4. Formato de instrucción tipo B, dos operandos.

El formato de instrucción tipo B se muestra en la Figura 4. Su tamaño es de 16 Bits. Tiene el código

de operación, y dos operandos. Ambos operandos son registros internos de propósito general. No

existe un registro de destino. Existen 3 Bits que no se utilizan, y que el sistema ignora. Así, estos

pueden tener cualquier información.

4.2.1.3. Formato Tipo C.

Figura 5. Formato de instrucción tipo D, un operando inmediato de 16 Bits, y un destino.

El formato de instrucción tipo C se muestra en la Figura 5. Su tamaño es de 32 Bits. Tiene el código

de operación, un operando y un destino. Inmediato16 es un dato inmediato de 16 Bits de ancho, que

viene en la instrucción. Es el argumento de entrada de la instrucción. Destino es un registro interno de

propósito general. Existen 7 Bits que no se utilizan, y que el sistema ignora. Así, estos pueden tener

cualquier información.

4.2.1.4. Formato Tipo D.

18

Figura 6. Formato de instrucción tipo E, sin operandos ni destinos explícitos.

El formato de instrucción tipo D se muestra en la Figura 6. Su tamaño es de 16 Bits. Tiene únicamente

el código de operación, que indica al núcleo lo que se quiere hacer. Las instrucciones que usan este

formato no necesitan argumentos, pues no se hacen operaciones sobre ellos, o porque implícitamente

se conocen. Existen 11 Bits que no se utilizan, y que el sistema ignora. Así, estos pueden tener

cualquier información.

4.2.2. Código de Operación y Tipo de Operación

El código de operación es lo que permite a la Unidad de Control del sistema reconocer la operación

que está almacenada en la memoria. Estas instrucciones son decodificadas y luego, los operandos que

trae la instrucción son utilizados de acuerdo a lo que el código represente.[8]

Para el núcleo, se tienen cinco Bits para el código de operación. A su vez, el código de operación está

subdividido en varios campos que identifican la operación que se requiere hacer, en términos

generales. A continuación se muestran las diferentes operaciones generales que componen el código

de operación de las instrucciones.

4.2.2.1. Operación Aritmética o Lógica.

Todas las operaciones de tipo aritmético entran en este conjunto. Los códigos de operación son de la

forma 00XXX.

4.2.2.2. Operación Comparativa y Otras.

Todas las operaciones de tipo lógico y de comparación entre operandos entran en este conjunto.

Además, tres instrucciones que no entran en los demás grupos entran acá. Los códigos de operación

son de la forma 01XXX.

4.2.2.3. Operación con Memoria.

Todas las operaciones que se comuniquen con memoria entran en este conjunto, además de la

instrucción que maneja carga de datos inmediatos, por ser del mismo tipo. Los códigos de operación

son de la forma 10XXX.

4.2.2.4. Operación de Ramificación.

Todas las operaciones de ramificaciones entran en este conjunto. Los códigos de operación son de la

forma 11XXX.

4.2.3. Conjunto de Instrucciones

A continuación se hace una clasificación de todas las instrucciones que el núcleo es capaz de realizar,

de acuerdo al formato que utilizan. Para cada una de las instrucciones, se explica qué operaciones

realiza[5] y su modo de direccionamiento.

4.2.3.1. Instrucciones con Formato tipo A.

Para este tipo de formato se tienen en total 9 instrucciones diferentes.

4.2.3.1.1. Suma con Signo.

Esta instrucción realiza la suma binaria entre dos registros del conjunto de registros de propósito

general, y devuelve el resultado a un tercer registro. Los dos operandos se interpretan como datos con

signo, en representación de complemento a 2[5][8][10]. El modo de direccionamiento de la instrucción

es Directo, en Registros[5].¡Error! No se encuentra el origen de la referencia. El código de

operación para esta instrucción es 00001. El tipo de instrucción es Aritmética o Lógica. Sus siglas son

SADD.

19

Figura 7. Diagrama de flujo para instrucción SADD.

4.2.3.1.2. Suma sin Signo.

Esta instrucción realiza la suma binaria entre dos registros del conjunto de registros de propósito

general, y devuelve el resultado a un tercer registro. Los dos operandos se interpretan como datos sin

signo. El modo de direccionamiento de la instrucción es Directo, en Registros[5].¡Error! No se

encuentra el origen de la referencia. El código de operación para esta instrucción es 00000. El tipo

de instrucción es Aritmética o Lógica. Sus siglas son UADD.

20

Figura 8. Diagrama de flujo para instrucción UADD.

4.2.3.1.3. Resta con Signo.

Esta instrucción realiza la resta binaria entre dos registros del conjunto de registros de propósito

general, y devuelve el resultado a un tercer registro. Los dos operandos se interpretan como datos con

signo, en representación de complemento a 2[5][8][10]. El modo de direccionamiento de la instrucción

es Directo, en Registros[5].¡Error! No se encuentra el origen de la referencia. El código de

operación para esta instrucción es 00011. El tipo de instrucción es Aritmética o Lógica. Sus siglas son

SSUB.

21

Figura 9. Diagrama de flujo para instrucción SSUB.

4.2.3.1.4. Resta sin Signo.

Esta instrucción realiza la resta binaria entre dos registros del conjunto de registros de propósito

general, y devuelve el resultado a un tercer registro. Los dos operandos se interpretan como datos sin

signo. El modo de direccionamiento de la instrucción es Directo, en Registros[5].¡Error! No se

encuentra el origen de la referencia. El código de operación para esta instrucción es 00010. El tipo

de instrucción es Aritmética o Lógica. Sus siglas son USUB.

22

Figura 10. Diagrama de flujo para instrucción USUB.

4.2.3.1.5. AND Bit a Bit.

Esta instrucción realiza la operación lógica AND, Bit a Bit[4]¡Error! No se encuentra el origen de la

referencia., entre dos registros del conjunto de registros de propósito general, y devuelve el resultado

a un tercer registro. El modo de direccionamiento de la instrucción es Directo, en Registros[5].¡Error!

No se encuentra el origen de la referencia. El código de operación para esta instrucción es 00100. El

tipo de instrucción es Aritmética o Lógica. Sus siglas son AND.

Figura 11. Diagrama de flujo para instrucción AND.

23

4.2.3.1.6. OR Bit a Bit.

Esta instrucción realiza la operación lógica OR, Bit a Bit[4]¡Error! No se encuentra el origen de la

referencia., entre dos registros del conjunto de registros de propósito general, y devuelve el resultado

a un tercer registro. El modo de direccionamiento de la instrucción es Directo, en Registros[5].¡Error!

No se encuentra el origen de la referencia. El código de operación para esta instrucción es 00101. El

tipo de instrucción es Aritmética o Lógica. Sus siglas son OR.

Figura 12. Diagrama de flujo para instrucción OR.

4.2.3.1.7. NOR Bit a Bit.

Esta instrucción realiza la operación OR negada entre dos datos que se encuentran en registros de

propósito general. El resultado de la operación se guarda en el registro indicado por el argumento de

salida de la instrucción. El modo de direccionamiento de la instrucción es Directo, en

Registros[5].¡Error! No se encuentra el origen de la referencia. El código de operación para esta

instrucción es 00110. El tipo de instrucción es Aritmética o Lógica. Sus siglas son NOR.

24

Figura 13. Diagrama de flujo para instrucción NOR.

4.2.3.1.8. Load.

Esta instrucción carga un dato de 16 Bits desde la unidad de memoria del sistema, a uno de los

registros de propósito general. La dirección del dato a cargar corresponde a la suma de los dos

argumentos de entrada de la instrucción, que se encuentran en registros de propósito general. Ambos

datos se interpretan como datos con signo, representados en complemento a 2[5][8][10]. El dato se

carga en el registro indicado por el argumento de salida de la instrucción. El modo de

direccionamiento de la instrucción es de Registro, con Índice[5].¡Error! No se encuentra el origen

de la referencia. El código de operación para esta instrucción es 10000. El tipo de instrucción es Con

Memoria. Sus siglas son LD.

25

Figura 14. Diagrama de flujo para instrucción LD.

4.2.3.1.9. Store.

Esta instrucción almacena un dato de 16 Bits desde uno de los registros de propósito general, hasta la

unidad de memoria del sistema. La dirección del dato a almacenar corresponde a la suma de los dos

argumentos de entrada de la instrucción, que se encuentran en registros de propósito general. Ambos

datos se interpretan como datos con signo, representados en complemento a 2[5][8][10]. El dato a

almacenar se encuentra en el registro indicado por el argumento de salida de la instrucción. El modo

de direccionamiento de la instrucción es de Registro, con Índice[5].¡Error! No se encuentra el origen

de la referencia. El código de operación para esta instrucción es 10001. El tipo de instrucción es Con

Memoria. Sus siglas son STR.

26

Figura 15. Diagrama de flujo para instrucción STR.

4.2.3.2. Instrucciones con Formato tipo B

Para este tipo de formato se tienen en total 4 instrucciones diferentes.

4.2.3.2.1. Comparación con Signo.

Esta instrucción realiza la comparación entre dos registros del conjunto de registros de propósito

general. Se asume que ambos registros tienen datos con signo, en representación de complemento a

2[5][8][10]. El resultado de la comparación aparece en los Bits N y Z del registro de estado. El modo

de direccionamiento de los argumentos de la instrucción es Directo, en Registros[5].¡Error! No se

encuentra el origen de la referencia. El código de operación para esta instrucción es 01001. El tipo

de instrucción es Comparativa y Otras. Sus siglas son SCMP.

27

Figura 16. Diagrama de flujo para instrucción SCMP.

4.2.3.2.2. Comparación sin Signo.

Esta instrucción realiza la comparación entre dos registros del conjunto de registros de propósito

general. Se asume que ambos registros tienen datos sin signo. El resultado de la comparación aparece

en los Bits N y Z del registro de estado. El modo de direccionamiento de la instrucción es Directo, en

Registros[5].¡Error! No se encuentra el origen de la referencia. El código de operación para esta

instrucción es 01000. El tipo de instrucción es Comparativa y Otras. Sus siglas son UCMP.

Figura 17. Diagrama de flujo para instrucción UCMP.

4.2.3.2.3. Ramificación, menor qué.

Esta instrucción cambia el valor del Program Counter, dependiendo de banderas del registro de

estado. Si el valor del Bit N es 0, no se hace nada. De lo contrario, se genera una suma entre el valor

del registro indicado por Operando1 y el valor del registro indicado por Operando2 de la instrucción.

Esta operación trata ambos operandos como datos sin signo, y no genera overflow aritmético. Si se

28

desea hacer una resta, será necesario que el dato en el segundo operando sea el negativo en

complemento a 2 del número que se desea restar[5][8][10]. El modo de direccionamiento de la

instrucción es Directo, en Registros[5].¡Error! No se encuentra el origen de la referencia. El código

de operación para esta instrucción es 11000. El tipo de instrucción es De Ramificación. Sus siglas son

JLT.

Figura 18. Diagrama de flujo para instrucción JLT.

4.2.3.2.4. Ramificación, igual.

Esta instrucción cambia el valor del Program Counter, dependiendo de banderas del registro de

estado. Si el valor del Bit Z es 0, no se hace nada. De lo contrario, se genera una suma entre el valor

del registro indicado por Operando1 y el valor del registro indicado por Operando2 de la instrucción.

Esta operación trata ambos operandos como datos sin signo, y no genera overflow aritmético. Si se

desea hacer una resta, será necesario que el dato en el segundo operando sea el negativo en

complemento a 2 del número que se desea restar[5][8][10]. El modo de direccionamiento de la

instrucción es Directo, en Registros[5].¡Error! No se encuentra el origen de la referencia. El código

de operación para esta instrucción es 11111. El tipo de instrucción es De Ramificación. Sus siglas son

JEQ.

29

Figura 19. Diagrama de flujo para instrucción JEQ.

4.2.3.3. Instrucciones con Formato tipo C

Para este tipo de formato se tiene en total 1 instrucción.

4.2.3.3.1. LoadInmediato16.

Esta instrucción se encarga de cargar un valor inmediato de 16 Bits en cualquiera de los registros de

propósito general. El valor que tenía el registro es reemplazado por el dato inmediato. El modo de

direccionamiento de la instrucción es Inmediato[5].¡Error! No se encuentra el origen de la

referencia. El código de operación para esta instrucción es 10110. El tipo de instrucción es Con

Memoria. Sus siglas son LDI.

30

Figura 20. Diagrama de flujo para instrucción LDI.

4.2.3.4. Instrucciones con Formato tipo D

Para este tipo de formato se tienen en total 4 instrucciones diferentes.

4.2.3.4.1. Habilitar Interrupciones.

Esta instrucción se encarga de cambiar el Bit IE en el registro de estado, al valor 1. Este Bit maneja la

habilitación de interrupciones. No tiene operandos explícitos, y su único operando, el Status Register,

es implícito. Esta instrucción no tiene modo de direccionamiento[5].¡Error! No se encuentra el

origen de la referencia. El código de operación para esta instrucción es 01101. El tipo de instrucción

es Comparativa y Otras. Sus siglas son IENA.

Figura 21. Diagrama de flujo para instrucción IENA.

4.2.3.4.2. Deshabilitar Interrupciones.

Esta instrucción se encarga de cambiar el Bit IE en el registro de estado, al valor 0. Este Bit maneja la

habilitación de interrupciones. No tiene operandos explícitos, y su único operando, el Status Register,

es implícito. Esta instrucción no tiene modo de direccionamiento[5].¡Error! No se encuentra el

31

origen de la referencia. El código de operación para esta instrucción es 01100. El tipo de instrucción

es Comparativa y Otras. Sus siglas son IDIS.

Figura 22. Diagrama de flujo para instrucción IDIS.

4.2.3.4.3. Retorno de Interrupciones.

Esta instrucción restaura el valor del Program Counter y del registro de estado, desde sus respectivos

registros de almacenamiento dedicados. En caso de intentar retornar desde afuera de una interrupción,

se genera una excepción. Esta instrucción no tiene modo de direccionamiento[5].¡Error! No se

encuentra el origen de la referencia. El código de operación para esta instrucción es 01110. El tipo

de instrucción es Comparativa y Otras. Sus siglas son RTI.

32

Figura 23. Diagrama de flujo para instrucción RTI.

4.2.3.4.4. No Operación.

Esta instrucción no hace ninguna acción. En el momento de ser interpretada por la Unidad de Control,

se empieza a realizar el proceso de búsqueda de la siguiente instrucción en memoria. Esta instrucción

no tiene modo de direccionamiento[5]. ¡Error! No se encuentra el origen de la referencia. El código

de operación para esta instrucción es 01010. El tipo de instrucción es Comparativa y Otras. Sus siglas

son NOP.

Figura 24. Diagrama de flujo para instrucción NOP.

33

4.2.3.5. Otras Operaciones

El núcleo es capaz de realizar algunas otras operaciones que no clasifican como instrucciones, pues no

son acciones que el usuario puede programar para utilizar de forma directa. A continuación se habla de

estas operaciones especiales.

4.2.3.5.1. Secuencia de Arranque del Sistema.

Cuando se enciende el sistema, aplicando una fuente de voltaje para su polarización, éste puede estar

en cualquier estado y puede estar realizando cualquier cosa. Por esto, es necesario reiniciarlo. Para

esto, una señal externa debe iniciar todos los Flip Flops del sistema en un valor conocido.

Los registros que lo requieran deben iniciarse. El único registro que requiere iniciarse en un valor

conocido es el Status Register, que se inicia en el valor “00000”. El Program Counter no requiere

iniciar su valor, pues sobre este se carga un valor específico en los primeros pasos de la Unidad de

Control. Los demás registros internos de propósito general no se inician en ningún valor, y se asume

que no contienen información válida. El registro de instrucciones no requiere iniciarse tampoco, pues

va a recibir un valor apropiado en el momento en que la Unidad de Control termine con el proceso de

inicialización[5]¡Error! No se encuentra el origen de la referencia..

La inicialización implica que las interrupciones están deshabilitadas por defecto cuando el núcleo

empieza a funcionar. Esto se hace con el fin de evitar que cualquier tipo de ruido o metaestabilidad

pueda interrumpir al sistema mientras este se estabiliza.

Es necesario poner a la Unidad de Control en su estado inicial. La misma señal externa se encarga de

que los Flip Flops de toda la Unidad de Control estén en los valores apropiados que le permitan

funcionar adecuadamente. Luego, en el primer paso, el núcleo carga desde la dirección de memoria

0x0000 el valor del Program Counter donde se encuentra la primera instrucción, y lo almacena.

Inmediatamente esto ocurre, el sistema empieza la ejecución de instrucciones. Primero debe revisar las

interrupciones. Dado que las interrupciones no están activas al inicio del sistema, éste procederá a

buscar la primera instrucción en memoria. Siempre que la señal externa sea activada, la Unidad de

Control es reiniciada y el sistema arranca de nuevo.

Figura 25. Diagrama de flujo de la secuencia de arranque del sistema.

34

4.2.3.5.2. Búsqueda de Instrucciones en Memoria.

Una vez que el sistema ha iniciado su funcionamiento, debe realizar la búsqueda de la primera

instrucción que ha de ser ejecutada. Esta instrucción viene desde la memoria, y corresponde a la

posición de la misma, apuntada por el Program Counter. Esta instrucción tiene un tamaño de dos o

cuatro Bytes. Para poder almacenar toda la instrucción, es necesario acceder a memoria una o dos

veces, dependiendo de la instrucción. Así, el Program Counter avanza una o dos veces al cargar una

instrucción. Dado que el Program Counter es un registro de 16 Bits[6]¡Error! No se encuentra el

origen de la referencia., no es necesario alinear las instrucciones más allá de lo necesario por cada

espacio de memoria como tal. Es decir, dada la organización de memoria en la que cada posición de

memoria ocupa 16 Bits[5]¡Error! No se encuentra el origen de la referencia., junto con el hecho de

que el Program Counter cuenta dirección por dirección, éste último puede direccionar cualquier

posición de memoria. Así, solo es necesario que las instrucciones estén alineadas a una posición, pues

es la unidad más pequeña de memoria que puede direccionar el Program Counter. Cuando esta

operación empieza, el Program Counter tiene almacenada la dirección de los 16 Bits más

significativos de la instrucción que se busca y que se ejecutará; cuando la operación ha terminado, el

Program Counter tiene almacenada la dirección de los 16 Bits más significativos de la siguiente

instrucción que se debe buscar.

Solo se ejecutará satisfactoriamente una búsqueda de instrucciones si el OpCode de la instrucción

leída existe. De lo contrario, se intentará una lectura de la misma posición de memoria, para confirmar

que no fue un error de lectura de memoria. Si persiste el OpCode ilegal, se genera una excepción, y no

se cargan instrucciones.

35

Figura 26. Diagrama de flujo de la búsqueda de instrucciones en memoria.

4.2.3.5.3. Interrupciones.

Existe un total de tres posibles fuentes externas de interrupción. Las interrupciones se atienden

siempre antes del comienzo del ciclo de búsqueda de instrucciones, de la Unidad de Control. Cuando

una de las posibles fuentes de interrupción está activa, y las interrupciones están habilitadas, el núcleo

copia el valor que tiene en el Program Counter a un registro especial, dedicado para esta tarea, PC_C.

Luego, copia el registro de estado a un registro análogo, SR_C. Después de esto, se reinicia el registro

de estado (banderas de negativo y cero, en el valor lógico 0), y se deshabilitan las interrupciones

(bandera del registro de estado de habilitación de interrupciones en 0). Luego, dependiendo de la

36

fuente de interrupción, se busca en posiciones de memoria desde 0x0001 hasta 0x0003, por la

dirección de memoria que contenga el inicio de la rutina de atención. Es decir, las interrupciones son

vectorizadas, con vectores en las posiciones 0x0001, 0x0002 y 0x0003. El valor encontrado en el

vector es copiado al Program Counter. Si todas las entradas están en cero, no existe interrupción.

Este esquema tiene como consecuencia que solo es posible ingresar a una sola de las rutinas de

interrupción a la vez, y no es posible anidarlas y continuar con la ejecución normal del programa, pues

una segunda interrupción entrando cuando ya se está atendiendo otra, eliminará el estado del núcleo,

previo a la primera interrupción.

Figura 27. Diagrama de flujo de atención a interrupciones.

4.2.3.5.4. Excepciones.

Existen tres fuentes de excepciones internas, no enmascarables. Por un lado está la ejecución de

OpCode ilegal, que ocurre siempre que la Unidad de Control encuentre en una instrucción un código

de operación que no corresponde a ninguna instrucción implementada. Otra ocurre por overflow

aritmético. La tercera ocurre por intentar retornar de una interrupción cuando no se está atendiendo a

una. Todas pueden ocurrir a pesar de tener las interrupciones deshabilitadas. Sin embargo, igual que en

una interrupción, solo es posible atender una a la vez, por lo cual las rutinas de atención a excepciones

deben estar escritas de manera que no generen otra excepción dentro de sí mismas.

En el caso de ejecución de OpCode ilegal, la Unidad de Control se encarga de reintentar la lectura de

la misma posición de memoria desde la cual se leyó el código de operación erróneo, con el fin de

37

confirmar que en efecto este es el valor almacenado en memoria, y no que hubo un error de lectura. A

la vez, el Bit CT en el Status Register se activa, con el fin de indicar que no se ha leído

satisfactoriamente la instrucción y se hará otro intento. En caso de que en la segunda ocasión no se

reconozca el código de operación, se genera una excepción. La Unidad de Control genera una

interrupción de forma interna, almacenando su estado actual en los registros designados para eso, y

mueve el Program Counter a una dirección fija, con el valor 0x0004. Para la atención a la excepción

se apartan diez espacios de memoria contiguos, pues después de este espacio está el espacio para

atención a excepción por overflow aritmético. En este espacio, el usuario se encarga de realizar

cualquier operación que crea conveniente para indicar que hubo un error en la ejecución del programa,

y que debe ser corregido. La detección de OpCode ilegal es indicada con un valor lógico de „1‟ en la

señal ILEGAL_OP_DET_OUT.

Figura 28. Diagrama de flujo de salto a excepción por ejecución de OpCode ilegal.

En el caso de un overflow aritmético, la Unidad de Control hace un salto sobre el Program Counter a

una dirección específica de memoria, por medio del mecanismo de interrupción. La dirección a la cual

salta es 0x000F. Para la atención a la excepción se apartan diez espacios de memoria contiguos, pues

después de este espacio está el espacio para atención a excepción por retorno inválido. Esta excepción

ocurre cuando el Bit de salida del a ALU V está en el valor lógico „1‟ después de una operación

aritmética. La operación aritmética es interrumpida de manera inmediata, y el resultado de la

operación no se almacena.

38

Figura 29. Diagrama de flujo de salto a excepción por overflow aritmético.

En el caso de un retorno de interrupción inválido, la Unidad de Control hace un salto sobre el Program

Counter a una dirección específica de memoria, por medio del mecanismo de interrupción. La

dirección a la cual salta es 0x001A. Por diseño, el usuario no tiene límite de espacio a partir de esta

dirección para las operaciones que deba realizar para atender la excepción. Sin embargo, por ser una

interrupción, está limitando la capacidad de respuesta del sistema si permanece mucho tiempo en esta

situación. Esta excepción ocurre cuando el Bit IA del Status Register está en el valor lógico „0‟ y se

intenta ejecutar una instrucción de salida de interrupción.

39

Figura 30. Diagrama de flujo de salto a excepción por retorno de interrupción inválido.

Para más información sobre las instrucciones (diagramas de tiempos de cada instrucción, y máquina

de estados de la Unidad de Control encargada de manejar las señales para cada instrucción) se puede

revisar las secciones de Anexos 9.3 y 9.4.

5. Pruebas de Simulación e Implementación

5.1. Simulaciones para el Núcleo

Después de haber implementado el sistema en VHDL, utilizando la herramienta de desarrollo Quartus

II de Altera, es necesario comprobar su funcionamiento. En primera instancia, se realiza una prueba

del sistema, sin memorias conectadas. De esta manera, se tiene solo el núcleo (Unidad de Datos y

Control conectadas), y en la simulación solo se reemplazan las entradas al sistema con señales que

corresponden a la manera como la memoria respondería, tanto para ciclos de fetch como para

almacenar y leer datos en esta. Por medio de señales modificadas por el usuario, ubicadas en los

tiempos correctos que el núcleo espera, para cargar instrucciones, datos y almacenar información en

memoria, el sistema funciona correctamente, de acuerdo a lo especificado por cada instrucción (Para

información detallada de cada instrucción ver la sección 4.2, Conjunto de Instrucciones. Para

información detallada del comportamiento en tiempo de las instrucciones, y mapa de transiciones de la

máquina de control del núcleo, ver las secciones de Anexos 9.3 y 9.4.

40

5.1.1. Simulación 1

En esta simulación se ejecutan instrucciones de búsqueda de instrucciones en memoria, carga de datos

inmediatos en los registros internos, almacenamiento de la información de los registros en memoria,

sumas y restas, con y sin signo, operaciones lógicas AND, OR y NOR, carga de datos desde memoria,

y la instrucción NOP. Para más detalles sobre la simulación, revisar la sección de Anexos 9.6.1.1.

5.1.2. Simulación 2

En esta simulación se comprueba el funcionamiento de los saltos condicionales en el flujo de

programa, que el núcleo es capaz de realizar. Se cargan tres datos en los registros internos. R0 siempre

contiene el valor 0x0000, R1 toma el valor 0x8000, R2 toma el valor 0x8000, R3 toma el valor

0x0010. Luego se realizan una serie de comparaciones e intentos de saltos condicionales entre los

registros R0, R1, R2 y R3, corroborando el funcionamiento correcto de los saltos condicionales y las

comparaciones entre datos. Para más detalles sobre la simulación, revisar la sección de Anexos

9.6.1.2.

5.1.3. Simulación 3

En esta simulación se comprueba el funcionamiento de las interrupciones, y de las instrucciones

asociadas. Inicialmente se habilitan las interrupciones con la instrucción IENA, y se comprueba que el

núcleo entra a cada una. Inmediatamente dentro de la interrupción, se sale de esta usando la

instrucción RTI. Luego de probar las tres interrupciones, se deshabilitan usando la instrucción IDIS.

Se comprueba que el núcleo no atiende las interrupciones entrantes, y continua con la ejecución del

programa normal. Para más detalles sobre la simulación, revisar la sección de Anexos 9.6.1.3.

5.1.4. Simulacion 4

En esta simulación se comprueba el funcionamiento de las excepciones, y de las instrucciones

asociadas. El núcleo no puede enmascarar la atención a excepciones. Esto se comprueba nunca

habilitando las interrupciones, y observando que, aún así, el núcleo detiene el flujo normal de

programa para atender las excepciones. Para más detalles sobre la simulación, revisar la sección de

Anexos 9.6.1.4.

De las cuatro simulaciones anteriores, se puede concluir que, tanto la unidad de control como la

unidad de datos del sistema están funcionando correctamente, en conjunto. Todas las operaciones que

realiza el sistema se ejecutan tal y como se especificó en el diseño del mismo, cumpliendo con los

mismos diagramas de tiempos, diagramas de flujos, y máquina de estados.

5.2. Simulaciones para el Sistema de Prueba

Una vez que el funcionamiento del núcleo ha sido validado, ahora es posible simular el programa del

protocolo de pruebas. Para esto, además de programar la memoria del sistema con el código del

programa, es necesario implementar un contador síncrono. El contador se alimenta con el mismo reloj

de todo el sistema. Tiene una señal de salida que corresponde a cuando el contador llega al valor

0x0200, y una señal de entrada que reinicia el conteo a 0x0000. Pues no hace falta contar más allá de

0x0200, es un contador que solo ocupa 10 Bits. Para más detalles sobre el funcionamiento del

contador, puede revisarse la sección de Anexos 9.8.

Teniendo un contador síncrono, solo resta programar la memoria interna del sistema con el programa

que ejecutará la prueba. Este código debe cargarse en memoria en lenguaje de máquina, posición por

posición, pues no se cuenta con herramientas de ensamble ni compilación de código. El esquema de

flujo del programa que se ha implementado para la prueba final del sistema se encuentra en la sección

de Anexos 9.5.1.

41

El código que ha de escribirse sobre la memoria está detallado a manera de lenguaje ensamblador,

para premitir mayor facilidad en su desarrollo, y depuración. El programa implementa el protocolo de

pruebas del sistema, y su código puede verse con detalle en la sección de Anexos 9.5.2.

El sistema, en base a su alta simplicidad, no reserva de manera activa gran cantidad de memoria para

uso específico, más allá de la dirección de inicio de ejecución, las direcciones de atención a

interrupciones y excepciones. Esto permite, y obliga, al usuario, a considerar cuánta memoria

reservará para programas, y cuánta para uso general del programa. El hecho de utilizar una FPGA para

la implementación física del sistema permite simplificar las conexiones de memoria y periféricos, pues

solo es requerido instanciarlos, y describir las interconexiones. Para dar total claridad al

funcionamiento de la prueba realizada, a continuación se muestra un diagrama de la distribución de

memoria para el sistema.

Figura 31. Distribución de memoria del sistema para el protocolo de pruebas.

Como se observa, solo es necesario tener en cuenta algunas pocas direcciones, por diseño. Lo demás

es escogido por el usuario. Para este caso, se escogió un tamaño de 512 espacios de memoria ROM,

que incluye los vectores de interrupción, rutinas de excepción e interrupción, y el programa como tal.

Para la memoria RAM, se escogieron 4096 posiciones, de las cuales las primeras 512 no son

utilizables, ya que para simplificar la implementación, no se desarrolló un controlador de memoria

muy complicado: este consiste en un multiplexor a la entrada del bus de lectura del núcleo, el cual

escoge entre la salida de la ROM o la RAM, dependiendo de la dirección que se esté leyendo.

Para reiniciar el contador, se lee o escribe a la posición de memoria 0x1000. Un bloque de lógica se

encarga de revisar el bus de direcciones del núcleo, de forma que cuando se acceda a esta posición de

memoria, entregue un „1‟ lógico. Este valor lógico se usa para reiniciar el contador.

42

Para poder programar la memoria ROM, utilizando la herramienta de desarrollo Quartus II, se requiere

un archivo con terminación .mif. Este archivo es directamente editable en Quartus II, posición por

posición. De esta manera, se puede escribir el programa en la memoria. Para mirar con detalle el

archivo de inicialización de memoria, se puede consultar la sección de Anexos 9.8.

Ya teniendo un entendimiento completo del protocolo de pruebas y de cómo el sistema debe

cumplirlo, se procede a realizar la simulación del código sobre el sistema. Para realizar la simulación

del sistema y observar en detalle su funcionamiento, puede verse la sección de Anexos 9.6.2.

Tal y como puede verse en la simulación, el sistema recorre las instrucciones del programa tal y como

corresponde, de acuerdo al planteamiento realizado para el protocolo de pruebas del sistema. Las

interrupciones entran en cualquier momento, y el núcleo se encarga de su atención, y luego retorna a la

ejecución normal del programa. Existen saltos a rutinas, con sus respectivos retornos, y todo ocurre

correctamente de acuerdo a lo que se espera, y de acuerdo a las especificaciones del núcleo. Esta

simulación valida, al menos de manera general, el funcionamiento correcto y esperado del núcleo. Lo

que se requiere ahora para su validación formal, es la programación del a FPGA con el sistema

simulado, y la revisión por medio de un analizador de estados lógicos, del funcionamiento del mismo.

5.3. Implementación y Ejecución del Sistema de Prueba en FPGA

Para la implementación del sistema en la FPGA Stratix II, se hizo uso de la tarjeta de desarrollo

incluida en el kit de desarrollo NIOS II Development Kit, Stratix II Edition[13]. Esta tarjeta trae el

chip FPGA Stratix II EP2S60F672C3, y un oscilador externo fijo a una frecuencia de 50MHz [14].

Esto significa que, aún cuando el sistema pueda ejecutarse a una velocidad de reloj superior, en la

práctica las pruebas solo se realizan a la velocidad entregada por el oscilador externo.

Como resultado de la compilación del sistema, se obtuvo una frecuencia de reloj máxima con valores

entre 96MHz y 100MHz, dependiendo de la ubicación del sistema en el chip. Para la realización de la

compilación, se activaron todas las posibles optimizaciones que provee la herramienta de desarrollo

Quartus II, en términos de desempeño del sistema, sacrificando espacio a cambio de obtener mayor

frecuencia de reloj. Se realiza este tipo de optimizaciones sabiendo que el sistema es suficientemente

pequeño como para poder sacrificar espacio por desempeño. Además, la FPGA sobre la que se está

trabajando tiene espacio de sobra para la ubicación del sistema, ocupando menos del 3% de los

elementos lógicos de la misma.

Para la verificación del sistema en tiempo real, sobre la FPGA, se utiliza un analizador de estados

lógicos. La herramienta Quartus II de Altera da la opción de utilizar un analizador de estados lógicos

interno, el cual es instanciado dentro de la FPGA como si fuera parte del proyecto. Para instanciar el

analizador de manera correcta, es necesario compilar el proyecto. Una vez se realiza la compilación

del sistema, se abre la herramienta Signal Tap II [15]. Con esta herramienta es posible observar

cualquier señal interna del sistema, sin necesidad de declararla explícitamente como señal de entrada o

salida para el proyecto, o de tener que exponerla a un pin de entrada/salida del chip. Esto permite

flexibilidad al momento de diseñar, pues no es necesario tener en cuenta el enrutamiento de las señales

que se desea observar, hacia el exterior, además de permitir una velocidad de ejecución en modo de

depuración en tiempo real mayor. Aparte de todo esto, es una herramienta muy sencilla de utilizar, que

no requiere instrumentos externos, ni cables adicionales, aparte de la interfaz JTAG con la cual se

programa la FPGA.

Para este caso, se realizó la visualización de las siguientes señales, que se consideran suficientes para

validar el buen funcionamiento del sistema, a la luz del protocolo de pruebas:

NUCLEO: MASTER_RESET, READ_ENA, WRITE_ENA, IRQ, BUS_READ, BUS_DIR,

BUS_WRITE.

43

R_BANK: W_ENA, PC_OUT, R0_OUT, R1_OUT, R2_OUT, R3_OUT, R4_OUT, R5_OUT,

R6_OUT, R7_OUT, R8_OUT, R9_OUT, R10_OUT, R11_OUT, R12_OUT, R13_OUT, R14_OUT.

Como puede compararse respecto a la simulación, solo se revisan señales que tienen que ver con el

banco de registros. Se considera que no es necesario monitorear más señales porque la gran mayoría

de lo que ocurre en el núcleo tiene que ver con, o se ve reflejado en, los registros internos y el

Program Counter, o se puede ver en el comportamiento del núcleo. Por ejemplo, para verificar que la

ALU está funcionando, se deben tomar dos datos del banco de registros, y el resultado puede

verificarse por medio de lo que el registro destino recibe como resultado de la operación. Lo mismo

ocurre para carga y almacenamiento de datos, y los saltos pueden verificarse con el Program Counter.

La habilitación de interrupciones se ve directamente en el comportamiento del sistema cuando se

recibe una, al igual que las excepciones. Reducir la cantidad de señales para visualizar permite enfocar

la atención solo en lo necesario, además de permitir mayores tiempos de muestreo de señales (los

resultados de muestreo se almacenan en la misma memoria interna de la FPGA, la cual no es

ilimitada).

Una vez escogidas las señales a visualizar, se escogió realizar una captura de datos de las mismas para

un total de 4096 muestras, desde el momento en que se de MASTER_RESET al sistema. Esto permite

ver el funcionamiento del sistema desde el comienzo, por un tiempo igual a (4096-512)*0.2u =

716.8us. La resta de 512 muestras ocurre porque el analizador, en su modo de funcionamiento por

defecto, está constantemente rastreando al sistema, de forma que almacena 512 muestras previas a la

señal de trigger, y 3584 posteriores. Así, los primeros 512 ciclos de reloj no se toman en cuenta. Esto

también permite observar la razón por la cual la simulación termina en 716.8us: de esta forma es

posible comparar los resultados con la simulación, por un tiempo de ejecución igual. Además de esto,

la simulación también se ejecutó con una frecuencia de reloj de 50MHz (período de 0.2us). Para

referencia sobre la observación del resultado del análisis en tiempo real del sistema, se puede ver

información en la sección de Anexos 9.7.

El resultado de la ejecución del sistema en tiempo real, sobre la FPGA, demuestra que el

funcionamiento del sistema es igual a lo esperado. Los resultados obtenidos son, cualitativamente,

iguales a los de la simulación: si bien los archivos generados, uno por la simulación, y el otro por la

ejecución en tiempo real, no son exactamente iguales, como consecuencia de que las interrupciones

entran al sistema en momentos diferentes para cada caso, el comportamiento general del sistema es

igual. Este resultado valida al sistema en términos del correcto funcionamiento del mismo, de acuerdo

a las especificaciones de diseño.

6. Análisis de Resultados

6.1. Desempeño Actual del Sistema

De acuerdo a lo que se ha diseñado, el resultado más evidente es que el sistema funciona de acuerdo a

lo esperado. Todas las instrucciones y demás operaciones se probaron, validando el funcionamiento de

la unidad de control. Además de esto, se ha validado también la interconexión entre la unidad de

control y el resto del sistema, con los datos que entran desde la memoria, que salen a memoria, o que

se procesan dentro del núcleo, siendo manipulados y movidos correctamente.

Respecto a la implementación física del conjunto de instrucciones, se puede decir que el tiempo de

ejecución promedio, en ciclos de reloj, para el conjunto de instrucciones es como se muestra a

continuación.

1. UADD: 4 ciclos de fetch, 1 ciclo de decodificación, 2 ciclos de ejecución.

44

2. USUB: 4 ciclos de fetch, 1 ciclo de decodificación, 2 ciclos de ejecución.

3. SADD: 4 ciclos de fetch, 1 ciclo de decodificación, 2 ciclos de ejecución.

4. SSUB: 4 ciclos de fetch, 1 ciclo de decodificación, 2 ciclos de ejecución.

5. AND: 4 ciclos de fetch, 1 ciclo de decodificación, 2 ciclos de ejecución.

6. OR: 4 ciclos de fetch, 1 ciclo de decodificación, 2 ciclos de ejecución.

7. NOR: 4 ciclos de fetch, 1 ciclo de decodificación, 2 ciclos de ejecución.

8. LD: 4 ciclos de fetch, 1 ciclo de decodificación, 3 ciclos de ejecución.

9. STR: 4 ciclos de fetch, 1 ciclo de decodificación, 2 ciclos de ejecución.

10. LDI: 4 ciclos de fetch, 1 ciclo de decodificación, 1 ciclo de ejecución.

11. UCMP: 2 ciclos de fetch, 1 ciclo de decodificación, 1 ciclo de ejecución.

12. SCMP: 2 ciclos de fetch, 1 ciclo de decodificación, 1 ciclo de ejecución.

13. JLT: 2 ciclos de fetch, 1 ciclo de decodificación, 1 o 2 ciclos de ejecución.

14. JEQ: 2 ciclos de fetch, 1 ciclo de decodificación, 1 o 2 ciclos de ejecución.

15. IDIS: 2 ciclos de fetch, 1 ciclo de decodificación, 1 ciclo de ejecución.

16. IENA: 2 ciclos de fetch, 1 ciclo de decodificación, 1 ciclo de ejecución.

17. RTI: 2 ciclos de fetch, 1 ciclo de decodificación, 1 ciclo de ejecución.

18. NOP: 2 ciclos de fetch, 1 ciclo de decodificación y ejecución.

19. IRQ: 1 ciclo de detección, 3 ciclos de carga de vector.

La cantidad de ciclos por instrucción, en promedio, es de 5.57 Ciclos de reloj por instrucción. En la

práctica, se puede esperar que este número alcance valores cercanos a seis ciclos de reloj por

instrucción, pues la mayoría de instrucciones que se espera que se ejecuten son operaciones

aritméticas y lógicas, tanto para resolución de algoritmos, como para cálculo de direcciones.

Dado que la arquitectura puede correr a una velocidad aproximada de 95MHz, sobre una FPGA Stratix

II, el resultado anterior se puede convertir en 5.57C/i * 0.010526 us/C = 0.058632 us/instrucción =

58.632 ns/instrucción.

Este resultado no es directamente comparable con ningún otro. La razón es que no existe manera de

compilar un programa tipo “benchmark” que permita la comparación entre esta arquitectura respecto

de otras. Sin embargo, es posible afirmar que este desempeño no es el mejor que se puede lograr, con

la arquitectura diseñada. Esto se puede afirmar a través de una serie de diferentes mejoras que pueden

realizarse. A continuación se detallan algunas.

6.2. Mejoras en la Unidad de Datos

La primera y mas evidente mejora que se puede realizar es la de implementar una lógica que maneje

automáticamente el modo de operación de la ALU, a partir del código de operación de la instrucción.

De esta manera, es más fácilmente optimizable la unidad de control, reduciendo instrucciones que

tienen que ver con la ALU (14 de las 18 instrucciones) en una cantidad igual a dos pasos por cada

instrucción.

45

Por otro lado, se puede también implementar una memoria externa con doble puerto. De esta manera,

es posible hacer la lectura de dos posiciones de memoria al mismo tiempo. Esto sería perfecto para las

instrucciones que ocupan dos espacios de memoria, ya que con una sola búsqueda se podrían tener

ambos segmentos de la instrucción. Esto genera un ahorro en la unidad de control de dos pasos para

todas las instrucciones de 32 Bits (10 instrucciones). Una memoria de estas características puede

explotarse aún más por medio del uso de pipelining[10], en casos en que se requiera acceder a dos

posiciones de memoria a la vez, en casos diferentes al descrito.

6.3. Mejoras en la Unidad de Control.

Lo más sencillo que puede realizarse para acelerar el proceso de ejecución de instrucciones es eliminar

las etapas de decodificación explícita, y ubicar la decodificación directamente en el último paso de la

búsqueda de instrucciones. Esto reduce en un ciclo de reloj el tiempo de ejecución de todas las

instrucciones.

Lo siguiente que puede modificarse es el diseño completo de la unidad de control. Se puede usar el

concepto de pipelining[10] para aumentar la cantidad de instrucciones por segundo que se ejecutan. La

segmentación de la unidad de control puede ser sencilla, de tres etapas, de forma que la complejidad

del sistema no aumente demasiado, pero que sí haya un aumento en el rendimiento. Caracterizar la

mejora en el desempeño del sistema después de esta modificación es más complicado.

En total, aplicando todas las optimizaciones, se puede estimar que el núcleo tendría una cantidad de

ciclos por instrucción igual aproximadamente a lo que se muestra a continuación.

1. UADD: 2 ciclos de fetch, 1 ciclo de ejecución.

2. USUB: 2ciclos de fetch, 1 ciclo de ejecución.

3. SADD: 2 ciclos de fetch, 1 ciclo de ejecución.

4. SSUB: 2 ciclos de fetch, 1 ciclo de ejecución.

5. AND: 2 ciclos de fetch, 1 ciclo de ejecución.

6. OR: 2 ciclos de fetch, 1 ciclo de ejecución.

7. NOR: 2 ciclos de fetch, 1 ciclo de ejecución.

8. LD: 2 ciclos de fetch, 2 ciclos de ejecución.

9. STR: 2 ciclos de fetch, 1 ciclos de ejecución.

10. LDI: 2 ciclos de fetch, 1 ciclo de ejecución.

11. UCMP: 2 ciclos de fetch, 1 ciclo de ejecución.

12. SCMP: 2 ciclos de fetch, 1 ciclo de ejecución.

13. JLT: 2 ciclos de fetch, 0 o 1 ciclo de ejecución.

14. JEQ: 2 ciclos de fetch, 0 o 1 ciclo de ejecución.

15. IDIS: 2 ciclos de fetch, 1 ciclo de ejecución.

16. IENA: 2 ciclos de fetch, 1 ciclo de ejecución.

17. RTI: 2 ciclos de fetch, 1 ciclo de ejecución.

46

18. NOP: 2 ciclos de fetch, decodificación y ejecución.

19. IRQ: 1 ciclo de detección, 3 ciclos de carga de vector.

Como se puede ver, todas las instrucciones pueden llegar a tener el mismo tiempo de ejecución,

excepto por la instrucción de carga de datos desde memoria, LD, la cual requiere 1 paso adicional.

Esto entrega un número de ciclos de reloj por instrucción igual a 3.05 ciclos por instrucción.

Asumiendo que la velocidad de reloj máxima no cambiará mucho, se puede hablar de un tiempo de

ejecución de 32.132ns/instrucción, lo cual es casi la mitad de lo que el sistema es capaz de hacer

actualmente. Una unidad de control segmentada puede requerir otro tipo de optimizaciones, por lo cual

evaluar la mejoría del sistema con esta característica es más complicado.

Como consecuencia de todo lo anterior, se puede decir que, aunque los resultados obtenidos por las

simulaciones y la ejecución en tiempo real del sistema validad su funcionamiento de acuerdo a las

especificaciones, el desempeño del sistema puede llegar a ser mucho mejor de lo que es actualmente.

7. Conclusiones

Existen varias áreas del diseño que hay que comentar. Por un lado, se encuentra el manejo de

interrupciones. El sistema es capaz de manejar tres fuentes de interrupción diferentes. Sin embargo, no

existe un mecanismo de anidamiento de interrupciones, y la única forma de atender un conjunto de

varias interrupciones simultáneas es por medio de la atención de una después de la otra. Esto tiene

como consecuencia que el tiempo de atención promedio para las interrupciones puede decrecer. Sin

embargo, en contrapeso a esto, el sistema actualmente cuenta con una manera muy veloz de cargar los

vectores de interrupción: puesto que no se tiene una pila en memoria en la cual se almacenan los

cambios de contexto, y solo se puede tener dos contextos diferentes simultáneamente en un programa

(el del programa principal y el de una interrupción a la vez), esto permite que el almacenamiento del

contexto del programa principal, cuando se recibe una interrupción, pueda almacenarse directamente

en registros internos del sistema, específicamente usados para esa tarea. Al evitar acceso a memoria, se

acelera el proceso de cambio de contexto. Si bien solo es posible manejar una interrupción a la vez, se

ha dejado el mecanismo de tres interrupciones externas, con el fin de dejar la posibilidad de

implementar un mecanismo que las maneje a la vez, sin tener que modificar las entradas al sistema, y

los bloques que manejan datos: solo se requiere un cambio en la unidad de control.

Otro aspecto que debe mirarse con detenimiento es el campo de las posibles optimizaciones que

puedan realizarse al sistema. La primera que debe tenerse en cuenta es incluir lógica combinatoria que

maneje el estado de funcionamiento de la ALU, solo con recibir el código de operaciones de la

instrucción, en cambio de tener que incluir pasos en la unidad de control para el manejo explícito de

ésta. Esto simplifica la unidad de control. No se realizó, pues no fue una opción considerada hasta muy

adelante en el desarrollo de las pruebas del sistema, y su inclusión no era viable en términos del

tiempo utilizado para hacerlo.

El uso de este bloque de lógica abre la posibilidad a una optimización mayor: segmentación de la

unidad de control. Esta posibilidad se exploró inicialmente, dada la natural composición de lo que

constituye un procesador tipo RISC, pero se concluyó que por falta de conocimiento sobre el tema, y

falta de tiempo para obtenerlo y aplicarlo correctamente, no se implementaría. Por medio de todas las

optimizaciones de las que se hablaron en los análisis de resultados, todas las instrucciones del sistema

se ejecutarían en los mismos tiempos, lo cual abre el campo para una implementación de una unidad

de control segmentada mucho más directo y sencillo.

Todas las demás decisiones de diseño que se tomaron, se hicieron a favor de tener un sistema que sea

flexible, pequeño y potencialmente veloz, con instrucciones fácilmente paralelizables. Solo se manejan

datos de un único tamaño (16 Bits), y solo hay reconocimiento limitado e implícito de dos tipos de

datos dentro del núcleo: datos enteros, sin signo, y datos enteros con signo representados en

47

complemento a 2. Esta distinción está implícita en las instrucciones de suma y resta, pues unas asumen

que los datos no tienen signo, y otras sí, conllevando en el segundo caso a una revisión de overflow

aritmético. Las operaciones con datos sin signo están diseñadas para ser usadas para realizar

operaciones con direcciones, donde el signo no existe, y las operaciones con datos con signo están

pensadas para ser usadas en algoritmos aritméticos que requieran manejo de números negativos. El

núcleo como tal no tiene capacidad para distinguir entre un dato con signo y otro sin signo. Es por esto

que el usuario está encargado de utilizar las instrucciones adecuadas para lo que requiera, y de hacer la

distinción por medio de las instrucciones que utilice para el manejo de los datos. Este enfoque ha sido

usado en otras arquitecturas, un ejemplo de las cuales se describe en la referencia [10].

Existen solo dos modos de direccionamiento en el sistema: direccionamiento inmediato, con datos de

16 Bits incluidos dentro de la instrucción, y direccionamiento directo, en registros o en memoria. No

hay operaciones con memoria que impliquen una doble operación: cuando se accede a esta, solo es

para almacenar o cargar datos.

El tamaño de las instrucciones es variable: es de uno o dos espacios de memoria. Inicialmente todas

las instrucciones iban a ser de 32 Bits, pero para la mitad del conjunto de instrucciones no tiene

sentido, pues no ocupan más de 16 Bits. Por medio de esta distinción, es posible acelerar el tiempo de

ejecución de las mismas.

El objetivo general era desarrollar una arquitectura tipo RISC, de propósito específico, para su

posterior implementación en un sistema digital embebido. Dadas las características del conjunto de

instrucciones, es bastante sencillo obtener una máquina digital tipo RISC a partir de éste: un conjunto

de instrucciones pequeño, versátil y que no tiene instrucciones complejas. Además, dado el tamaño del

núcleo, es fácil de implementar en cualquier tipo de sistema embebido: su tamaño lo hace un sistema

que no consume mucha potencia en su operación. Se ha dejado abierta la implementación de la

interfaz con memorias y periféricos, de forma que el usuario pueda realizar un diseño de los mismos

que se adapte a los requerimientos del sistema embebido que se desea implementar. Sin embargo, no

se cumple a cabalidad con el objetivo: el núcleo no tiene una unidad de control segmentada; es

secuencial. Además, existen optimizaciones que no se aplicaron, que generan un conjunto de

instrucciones más uniforme, respecto al tiempo de ejecución de cada instrucción.

En cuanto a los objetivos específicos planteados, las especificaciones técnicas detalladas fueron

generadas. El sistema ha sido detallado en todos los niveles posibles: desde el conjunto de

instrucciones, diagramas de bloques y de interconexión de bloques detallados de cada circuito del

sistema, con su funcionalidad explicada sin ambigüedades, hasta diagramas de tiempos que cada

instrucción debe cumplir, junto con todas las señales que intervienen y su estado en cada momento de

su ejecución, y la máquina de control del sistema, detallada paso a paso por medio del lenguaje AHPL.

También, el sistema fue implementado físicamente en un chip FPGA, y su funcionamiento fue

verificado con respecto a las simulaciones realizadas del mismo. Tanto las pruebas en simulación

como las pruebas sobre el chip FPGA permitieron validar las especificaciones del sistema.

8. Bibliografía

[1] C. H. Séquin, D. A. Patterson. “Design and Implementation of RISC I”. University of Bristol,

England, July 19-30, 1982.

[2] J. Cocke, V. Markstein. “The Evolution of RISC Technology at IBM”. IBM Journal of

Research and Development, Vol. 34, No. 1, 1990.

[3] MIPS Technologies. “MIPS32® Architecture For Programmers, Volume I: Introduction to the

MIPS32®”. Document Number: MD00082, Revision 2.60, June 25, 2008.

48

[4] Samuel O. Aletan. “An Overview of RISC Architecture”. Louisiana Tech University, 1992.

[5] Sivarama P. Dandamudi. “Guide to RISC Processors for Programmers and Engineers”. New

York, USA, 2005.

[6] ARM Limited. “ARM ARM7TDMI-S Reference Manual”. Revision r4p3, 2001.

[7] Compaq Computer Corporation. “The Alpha Architecture Handbook”. Version 4, October

1998.

[8] Francisco Viveros. Apuntes de Clase. Pontificia Universidad Javeriana, Bogotá, Agosto a

Noviembre de 2008.

[9] Luisa García. Apuntes de Clase. Pontificia Universidad Javeriana, Bogotá, Enero a Mayo de

2008.

[10] D. A. Patterson, J. L. Hennessy. “Computer Organization and Design”. San Franciso, USA,

2005, 3rd

Ed.

[11] Altera Corporation. “Internal Memory (RAM and ROM) User Guide”. California, USA,

November 2009. Internet link: http://www.altera.com/literature/ug/ug_ram_rom.pdf, Abril 5,

2010, 01:00 hrs.

[12] Altera Corporation. “Stratix II Device Handbook”. California, USA, 2009. Internet link:

http://www.altera.com/literature/hb/stx2/stratix2_handbook.pdf, Abril 5, 2010, 01:00 hrs.

[13] Altera Corporation. “NIOS II Development Kit, Stratix II Edition”. Internet link:

http://www.altera.com/products/devkits/altera/kit-niosii-2S60.html, Abril 9, 2010, 03:52 hrs.

[14] Altera Corporation. “NIOS Development Board Stratix II Edition Reference Manual”. Internet

link: http://www.altera.com/literature/manual/mnl_nios2_board_stratixII_2s60_rohs.pdf,

Mayo 19, 2010, 23:49 hrs.

[15] Altera Corporation. “Design Debugging Using the SignalTap II Embedded Logic Analyzer”.

Quartus II 9.1 Handbook, Volume 3, Chapter 15. Internet link:

http://www.altera.com/literature/hb/qts/qts_qii53009.pdf, Mayo 7, 2010, 02_39 hrs.

9. Anexos

9.1. Anexo: ALU

La Unidad Aritmética Lógica (ALU, por sus siglas en inglés) es la que se encarga de realizar las

operaciones matemáticas que permiten la ejecución de algoritmos que permitan la resolución de

problemas. Es importante poner atención a los detalles de su implementación, pues de ésta depende

gran parte del desempeño del núcleo. No es posible lograr un buen desempeño si ésta se encuentra en

un nivel inferior respecto a los demás sistemas.

La ALU debe ser capaz de realizar operaciones de suma y resta binarias. También, debe ser capaz de

realizar tres funciones lógicas básicas, AND, OR y NOR, y comparación entre dos operandos. Debe

poder determinar si la suma o resta de dos operandos tratados con signo genera overflow. El tamaño de

datos que debe ser capaz de operar es de 16 Bits. Para realizar la operación de resta, se utiliza

matemática de los números binarios en complemento a 2, así como para la representación de números

negativos[5][8][10]. A continuación se muestra un diagrama de bloques general de la ALU.

49

Figura 32. Diagrama de bloques general de la ALU.

El sistema tiene dos señales de entrada de datos: OP1 de 16 Bits, corresponde al primer operando; OP2

de 16 Bits, corresponde al segundo operando. Tiene cuatro señales de salida de datos: N de 1 Bit,

corresponde al Bit de comparación entre operandos; Z de 1 Bit, corresponde a igualdad entre

operandos: V de 1 Bit, representa overflow aritmético en una suma o resta con instrucciones que tratan

los datos como dígitos con signo en complemento a 2; ALU_OUT de 16 Bits, es el resultado de alguna

de las operaciones que la ALU es capaz de realizar Tiene cuatro señales de control: SEL_OP2_C de 1

Bit; SEL_CARRY_C de 1 Bit; SEL_COMPARACIÓN de 1 Bit; ALU_SEL_OUT_C de 2 Bits. Por

medio de este sistema es posible realizar las diferentes operaciones aritméticas/lógicas de las que se

habló anteriormente.

9.1.1. U_ARIT

A continuación se muestra el bloque U_ARIT con más detalle.

50

Figura 33. Bloque U_ARIT mostrado en detalle.

El bloque U_ARIT recibe dos operandos. Luego, genera su suma o resta binaria, por medio de 16 full

adders en paralelo, y entrega su resultado a la salida. Este resultado será una suma, si la instrucción

escogida es de suma, o una resta. Para realizar la resta, por dentro se tiene un bloque, NEGADOR, que

niega la señal OP2, convirtiéndola en su negativo en complemento a 1. Esta señal luego pasa al

sumador interno, SUMADOR en donde el Bit de acarreo de entrada se pone en 1, con el fin de lograr

que el segundo dato sea negativo, representado en complemento a 2.

El bloque tiene tres salidas, RES_ARIT, que lleva el resultado de la operación aritmética,

CARRY_OUT, que lleva el Bit de acarreo de la operación, y CARRY_14, que en conjunto con la

anterior señal, sirven para que el circuito de detección de overflow funcione.

Dependiendo de la instrucción, la señal de acarreo que entra al bloque SUMADOR es diferente. Si se

requiere hacer sumas, la señal de entrada escogida será el valor lógico „0‟. Si se requiere hacer una

resta, se usa la señal „1‟ para obtener el complemento a 2 del segundo operando.

9.1.1.1. NEGADOR.

Bloque de lógica combinatoria que se encarga de negar su entrada. Tiene una señal de entrada de

datos: OP2 de 16 Bits. Tiene una señal de salida de datos: OP2_NEG de 16 Bits.

9.1.1.2. SEL_OP2.

Multiplexor 2:1 de 16 Bits de ancho. Se encarga de escoger el segundo operando que entra al sumador,

dependiendo de si se realiza una suma o una resta. Tiene dos señales de entrada de datos: OP2 de 16

Bits; OP2_NEG de 16 Bits. Tiene una señal de salida de datos: SEL_OP2_OUT de 16 Bits. Tiene una

señal de control: SEL_OP2_C de 1 Bit, se encarga de escoger cual de las dos entradas de datos estará a

la salida del bloque.

9.1.1.3. SEL_CARRY.

Multiplexor 2:1 de 1 Bit de ancho. Se encarga de escoger la señal de acarreo de entrada para el

sumador, dependiendo de si se realiza una suma o una resta. Tiene dos señales de entrada de datos: el

51

valor binario „0‟; el valor binario „1‟. Tiene una señal de salida de datos: SEL_CARRY_OUT de 1 Bit.

Tiene una señal de control: SEL_CARRY_C de 1 Bit, se encarga de escoger cual de las dos entradas

de datos estará a la salida del bloque.

9.1.1.4. SUMADOR.

Para acelerar el proceso de cálculo de una operación, se implementa un sistema de look-ahead carry,

el cual permite reducir el tiempo que se necesita para calcular el acarreo de los bits más significativos,

dado que ya no es necesario esperar a que éste se propague por toda la lógica, desde el Bit menos

significativo, hasta el más significativo. Sin embargo, no es posible hacer una operación de acarreo

predictivo para todos los 16 Bits, pues la lógica involucrada en el cálculo de acarreos predictivos, a

medida que se calculan Bits más significativos, crece muy rápido. La idea es acelerar el proceso de

cálculo, pero también tener la simplicidad presente. Es por esto que se implementa como se muestra,

con acarreo predictivo para grupos de 4 Bits. Así, se tiene una ganancia en velocidad de cálculo,

manteniendo suficientemente baja la cantidad de compuertas lógicas requeridas para acelerar el

proceso. A continuación se muestra un grupo de cuatro sumadores con acarreo predictivo, y sus

ecuaciones. Las entradas y salidas se representan con las letras A, B y O respectivamente, para evitar

que las ecuaciones extiendan demasiado, solo por los nombres de las variables usadas.[8][10]

Figura 34. Bloque de cuatro sumadores, con acarreo predictivo.

0 0 0 0

1 1 1 1

2 2 2 2

3 3 3 3

( ) ( )

( ) ( )

( ) ( )

( ) ( )

O A xor B xorC

O A xor B xorC

O A xor B xorC

O A xor B xorC

0 0 0

1 1 1

2 2 2

3 3 3

g A B

g A B

g A B

g A B

0 0 0

1 1 1

2 2 2

3 3 3

p A B

p A B

p A B

p A B

52

1 0 0 0

2 1 1 0 1 0 0

3 2 2 1 2 1 0 2 1 0 0

4 3 3 2 3 2 1 3 2 1 0 3 2 1 0 0

( )

( ) ( )

( ) ( ) ( )

( ) ( ) ( ) ( )

C g p C

C g p g p p C

C g p g p p g p p p C

C g p g p p g p p p g p p p p C

Para lograr el sumador de 16 Bits, con cuatro sumadores de cuatro Bits con acarreo predictivo, es

necesario interconectar las líneas de acarreo. La línea de acarreo de salida del primer bloque de cuatro

Bits se conecta directamente a la línea de acarreo de entrada del segundo bloque de cuatro Bits, y así

sucesivamente. La línea de acarreo del cuarto bloque de cuatro Bits es la línea de acarreo de salida del

sumador completo. El bloque SUMADOR, dividido en sus cuatro bloques, se muestra en la Figura 35

a continuación.

Figura 35. Bloque SUMADOR, dividido en 4 bloques que suman 4 Bits, cada uno con acarreo predictivo.

El bloque tiene tres señales de entrada de datos: OP1 de 16 Bits; SEL_OP2_OUT de 16 Bits;

SEL_CARRY_OUT de 1 Bit. Tiene tres señales de salida de datos: RES_ARIT de 16 Bits;

CARRY_14 de 1 Bit; CARRY_OUT de 1 Bit.

9.1.2. U_AND, U_OR, U_NOR

Los bloques de lógica U_AND, U_OR y U_NOR funcionan igual. Para ambos, se tienen dos datos de

entrada, OP1 y OP2. Cada bloque realiza una función lógica entre ambos datos, Bit a Bit, y entrega el

resultado en sus respectivas salidas, RES_AND, RES_OR y RES_NOR, señales de 16 Bits.

9.1.3. LOGICA_OVERFLOW

El bloque LOGICA_OVERFLOW recibe los Bits de acarreo más significativos del bloque U_ARIT.

El bloque se encarga de calcular si existe overflow en la operación aritmética, y entrega el resultado en

53

su salida, V. El resultado del bloque solo se debe tener en cuenta cuando se hacen operaciones

aritméticas entre los datos, tratados con signo[8]. Para saber si una operación entre datos sin signo ha

cometido overflow, se utiliza el Bit de acarreo de la operación[5]. Sin embargo, para las operaciones

que tratan los datos sin signo, la condición de overflow se ignora. La ecuación lógica del bloque

LOGICA_OVERFLOW es ( _ ) ( _14)V CARRY OUY xor CARRY .

Tiene dos señales de entrada de datos: CARRY_14 de 1 Bit; CARRY_OUT de 1 Bit. Tiene una señal

de salida de datos: V de 1 Bit.

9.1.4. SEL_COMPARACION

Para lograr la comparación entre dos datos, es necesario saber de antemano si los datos son tratados

como con signo o sin signo, pues dependiendo de esto, la forma de detectar un resultado para la

comparación cambia. Para esto, se necesitan 2 bloques diferentes, uno que escoja el resultado de la

comparación dependiendo de si los datos son tratados con signo o sin signo, y otro bloque que

compare si ambos datos son iguales o no (en este caso no interesa el signo, pues si son iguales, deben

ser iguales Bit a Bit). Estos bloques se encargan de actualizar los Bits del Status Register que indican

si una operación dio cero, o si un número es mayor que otro.

El bloque SEL_COMPARACION escoge entre las dos posibles formas de detectar si un dato es mayor

o menor que otro. Si los datos se tratan con signo, este bloque escoge el Bit más significativo de la

resta de los datos, RES_ARIT[15],que corresponde al signo. Así, si la resta es positiva, N será 0 y OP1

es mayor a OP2. Si la resta es negativa, N será 1 y OP1 es menor que OP2. Por otro lado, si los datos

son tratados sin signo, el bloque escoge el Bit de acarreo de salida del bloque U_ARIT. La razón para

esto es que si ambos datos son tratados sin signo, la única forma de saber si OP1 es menor que OP2, es

si la resta genera un oveflow. Dado que para datos sin signo, el Bit de acarreo se usa para detectar

overflow, es esta la forma más sencilla de detectar si un número es mayor que otro. Así, si OP1 es

mayor que OP2, no habrá overflow y CARRY_OUT será 0, haciendo que N sea 0. Si OP1 es menor

que OP2, sí habrá overflow, CARRY_OUT será 1, y N será 1. Tiene dos señales de entrada de datos:

CARRY_OUT de 1 Bit; RES_ARIT[15] de 1 Bit. Tiene una señal de salida de datos: N de 1 Bit.

Tiene una señal de control: SEL_COMPARACION_C de 1 Bits, se encarga de escoger cual resultado

de las dos posibles formas de comparación va a la salida.

9.1.5. LOGICA_COMPARACION_CERO

El bloque LOGICA_COMPARACION_CERO se encarga de detectar igualdad entre dos datos. La

forma en que lo hace es por medio del resultado de la resta entre dos datos. Cuando los datos son

iguales, la entrada será igual a 0x0000. Así, para poder detectar igualdad, se usa la siguiente ecuación

en este bloque:

_ [15]' _ [14]' _ [1]' _ [0]'Z RES ARIT RES ARIT RES ARIT RES ARIT

Tiene una señal de entrada de datos: RES_ARIT de 16 Bits. Tiene una señal de salida de datos: Z de 1

Bit.

9.1.6. ALU_SEL_OUT

Este bloque es un multiplexor 4:1 de 16 Bits de ancho. Tiene cuatro señales de entrada de datos:

RES_ARIT de 16 Bits; RES_AND de 16 Bits; RES_OR de 16 Bits; RES_NOR de 16 Bits. Tiene una

señal de salida de datos: ALU_OUT de 16 Bits. Tiene una señal de control: ALU_SEL_OUT_C de 2

Bits, se encarga de dejar pasar una sola de las cuatro entradas de datos, a la señal de salida de la ALU.

54

9.2. Anexo: Registros Internos de Propósito General

El núcleo debe tener un conjunto de bloques que le permitan almacenar información de forma

temporal para poder realizar diferentes funciones, almacenar resultados o determinar el estado de

algunos subsistemas. A continuación se describirán todos los registros internos de propósito general

que tiene el sistema.[9]

9.2.1. Estructura de Entrada de Datos

En el diagrama en bloques del sistema se describió de forma general todos los bloques que tiene el

núcleo. Entre estos se incluyen los registros. Sin embargo, no se dio una descripción detallada del

conjunto de registros de propósito general, R_BANK. Para empezar, todos los registros están hechos a

partir de Flip Flops del tipo Data. No es necesario utilizar Flip Flops más complejos (tales como los J-

K) pues con los Data es posible realizar todo lo que se necesita, sin que sea necesaria circuitería digital

adicional. Esto además debe mejorar el tiempo de respuesta de cada Flip Flop[9]. Por último, todos

deben tener señal de habilitación, que les permita recibir información solo en el momento en que sea

necesario, e ignorarla cuando no. A continuación se muestra un diagrama de bloques del sistema de

registros internos de propósito general R_BANK. Por simplicidad, en la Figura 36 solo se muestran las

señales que portan datos. Las señales de control se muestran más adelante.

Figura 36. Diagrama de bloques general del conjunto de registros internos de propósito general.

9.2.1.1. R0 a R15 y PC_C.

Como se puede ver, hay un total de 16 registros internos de propósito general, todos de 16 Bits de

ancho. De estos, 14 tienen exactamente la misma estructura. Tienen todos en común la misma señal de

entrada de datos: BUS_IN de 16 Bits. Tienen cada uno una señal de salida de datos independiente:

Rn_OUT de 16 Bits, donde la “n” representa un número entre 1 y 14.

Existen dos registros que pueden ser tratados como de propósito general, pero que en realidad

funcionan para tareas específicas. El registro R15 es el Program Counter, registro encargado de

mantener la dirección de memoria de la instrucción actual de ejecución. Tiene un sumador dedicado a

incrementar su valor en 1 o 2 después de la búsqueda de una instrucción en memoria, un multiplexor a

su entrada, y un registro de almacenamiento temporal de su dato. Tiene una señal de entrada:

MUX_PC_OUT de 16 Bits. Tiene una señal de salida: PC_OUT de 16 Bits.

55

Hay además un registro que no es directamente direccionable: PC_C funciona cuando entra una

solicitud de atención a interrupción o excepción. Su función es la de guardar el dato almacenado por el

registro R15 de forma temporal, mientras que la interrupción es atendida. Luego, cuando se termina la

atención a la misma, R15 toma el valor almacenado en PC_C y continúa con el flujo normal del

programa. El registro PC_C tiene una señal de entrada de datos: PC_OUT de 16 Bits. Tiene una señal

de salida de datos: PC_C_OUT de 16 Bits. Tiene dos señales de control: PC_C_C, de 1 Bit; CLK, de 1

Bit, el reloj del sistema.

En la Figura 37 se muestra el diagrama general de todos los registros, excepto por R0. Este es un

registro interno de propósito general, pero no funciona como tal. Es una señal de salida, más no sirve

para almacenar información. Las salidas del banco de registros que referencian al registro R0 están

conectadas al valor 0x0000. Esto permite hacer diferentes operaciones que requieren de este valor, sin

necesidad de cargarlo explícitamente a un registro. Además, permite una forma rápida para copiar un

registro a otro, simplemente efectuando una suma del dato a copiar contra este registro, y almacenando

el resultado en otro. Tratar de escribir datos a este registro no tendrá ningún efecto en su valor.

Figura 37. Diagrama de bloques detallado de los registros R1 a R15.

9.2.1.2. PC_ADD.

Es un sumador dedicado específicamente a aumentar en 1 el valor del registro R15, cuando sea

necesario (después de la búsqueda de instrucciones en memoria). Se implementa por medio de 16 half-

adders en cascada, de la forma que se muestra en la Figura 38. Las entradas de cada bloque sumador

son X e Y, S corresponde a la salida, y C al bit de acarreo generado. Las entradas y salidas están

relacionadas por medio de las siguientes ecuaciones lógicas:

S = X xor Y

C = X ^ Y

El último sumador de la cadena no requiere tener señal de acarreo de salida. El retraso en el cálculo de

último Bit será de 16 veces el tiempo de propagación de una compuerta AND, pues esta es la cantidad

de compuertas que hay entre el primer acarreo generado y el último. Tiene una señal de entrada de

datos: PC_OUT de 16 Bits. Tiene una señal de salida de datos: PC_ADD_OUT de 16 Bits.

56

Figura 38. Diagrama Diagrama de bloques de PC_ADD.

9.2.1.3. MUX_PC.

La entrada al registro R15 está controlada por un multiplexor 8:1, que escoge una de diferentes

posibles entradas. Tiene seis entradas de datos: BUS_IN de 16 Bits, que corresponde a cuando se trata

el registro R15 como de propósito general; PC_ADD_OUT de 16 Bits, usada cuando es necesario

aumentar el valor del Program Counter; PC_C_OUT de 16 Bits, usada cuando se hace el retorno de

una interrupción o excepción; el valor 0x0004, que corresponde a la dirección de inicio de la rutina de

atención a la excepción causada por la ejecución de código de operación no definido; el valor 0x000F,

que corresponde a la dirección de inicio de la rutina de atención a la excepción causada por overflow

aritmético; el valor 0x001A, que corresponde a la dirección de inicio de la rutina de atención a la

excepción causada por intentar retornar de una interrupción cuando no se está dentro de una. Tiene

una señal de salida de datos: MUX_PC_OUT de 16 Bits. Tiene una señal de control: MUX_PC_C de

3 Bits, que se encarga de controlar cual de las entradas de datos se refleja a la salida.

9.2.2. Estructura de Control

9.2.2.1. BANK_DEC.

Para lograr almacenar la información en algún registro especificado por una instrucción, es necesario

poder habilitarlo de forma independiente. Para esto es necesario tener un decodificador, cuya entrada

será un número codificado en binario de 4 Bits, entre 0 y 15, y cuya salida serán 16 bits, cada uno

conectado a un registro. Además de esto, puesto que no siempre se requiere almacenar información en

algún registro, se hace una AND para cada registro, entre la señal que lo selecciona desde el

decodificador, y una señal que es la misma para todos los registros, W_ENA. Así, cuando W_ENA

esté en un alto lógico, y se esté seleccionando un registro por medio del decodificador, dicho registro

será escrito con el valor que se encuentre en el bus BUS_IN. Si alguna de las dos señales de control

está en un bajo lógico, el registro no será escrito.[10] La señal de salida del decodificador

57

correspondiente al registro R0 no está conectada a nada, pues el registro como tal no existe. El

decodificador tiene una señal de entrada de datos: MUX_DEC_OUT de 4 Bits. Tiene una señal de

salida de datos: BANK_DEC_OUT de 16 Bits.

9.2.2.2. MUX_DEC.

Algunas veces es necesario escribir específicamente el registro R15. Para lograr esto, se usa un

multiplexor 2:1 de 4 Bits, a la entrada del decodificador. Una entrada corresponde al registro indicado

por la instrucción. La otra entrada será el valor binario que indica que debe activarse el registro R15

para ese caso. Tiene dos señales de entrada de datos: R_INST[3:0] de 4 Bits; el valor binario „1111‟.

Tiene una señal de salida de datos: MUX_DEC_OUT de 4 Bits. Tiene una señal de control:

MUX_DEC_C de 2 Bits, que controla cual de las dos entradas se verá reflejada en la salida.

9.2.2.3. Control al registro PC_C.

El registro PC_C solo se activa en un caso específico, controlable fácilmente por medio de la Unidad

de Control con la señal PC_C_C, y por ende no requiere acceso por medio de la señal de escritura o de

alguna señal del decodificador. Además, no es un registro direccionable por el usuario.

Para lograr entender completamente su funcionamiento, se presenta un diagrama de bloques de las

señales de control en la Figura 39. Todas las señales de datos se omiten, pues ya han sido mostradas

anteriormente, además su inclusión complica el diagrama, impidiendo tener mayor claridad sobre su

funcionamiento.

Figura 39. Diagrama de bloques del esquema de control de habilitación del banco de registros.

58

El banco de registros internos de propósito general funciona de forma similar a como funciona una

unidad de memoria. Así, es necesario decodificar qué registro quiere cargarse con información, para

así poder aplicar la señal apropiada solo a ese registro.

Figura 40. Conexiones necesarias para escoger registros de salida del banco de registros.

9.2.3. Estructura de Salida de Datos

Lo último que hay que tener en cuenta es la selección de salidas para el banco de registros. La señal de

16x16 Bits, BUS_OUT contiene las 16 salidas de los 16 registros, cada una de 16 Bits. Sin embargo,

el banco de registros tiene 3 salidas diferentes. Dos de estas salidas pueden corresponder a cualquier

registro interno, mientras que la tercera salida es exclusivamente usada por el registro R15 para la

búsqueda de instrucciones en memoria.

9.2.3.1. MUX1 y MUX2.

Para lograr la selección de qué registro se desea llevar a las salidas, se utilizan dos multiplexores 16:1

de 16 Bits de ancho. Estos multiplexores conectan los registros de salida escogidos a la ALU para su

operación. El multiplexor MUX1, además lleva la salida escogida al registro R_OUT, cuando se desea

almacenar un dato en memoria. En la Figura 40 se muestra la configuración del banco de registros con

los dos multiplexores, y la salida del registro R15. Ambos multiplexores tienen la misma señal de

entrada de datos: BUS_OUT de 16x16 Bits. Tienen, respectivamente, estas señales de salida de datos:

OP1 de 16 Bits, para MUX1; OP2 de 16 Bits, para MUX2. Tienen, respectivamente, estas señales de

control: MUX_OP1_OUT de 4 Bits, para MUX1; R_INST[19:16] de 4 Bits, para MUX2.

9.2.3.2. MUX_OP1.

La señal de control que va al multiplexor MUX1 depende de la instrucción. Para controlar cuál de

estos dos valores corresponde a la salida, se utiliza otro multiplexor, conectado a la señal de control de

MUX1. Este es un multiplexor 2:1 de 4 Bits. Tiene dos señales de entrada de datos: R_INST[3:0] de 4

Bits; R_INST[23:20] de 4 Bits. Tiene una señal de salida de datos: MUX_OP1_OUT de 4 Bits. Tiene

una señal de control: OP1_SOURCE_C de 1 Bit, que controla cual de las dos señales de entrada se

refleja a la salida, indicando cual de las dos posibles opciones corresponde al registro que se desea

escoger como registro de salida.

59

9.3. Anexo: Diagramas de Tiempos

Es posible clasificar las instrucciones de dos maneras: por formato de instrucción, o por tipo de

operación que realiza. Para tener una idea general del comportamiento de las instrucciones, es

importante tener en cuenta la segunda organización.

Existen, en general, cinco tipos de instrucciones que el núcleo es capaz de realizar. Estas son:

instrucciones aritméticas, lógicas, de ramificación, con memoria, y otras que no pertenecen a ninguna

categoría. Sin embargo, como se verá más adelante, esta división es muy general, y hay algunas

instrucciones que, a pesar de caer dentro de una de las anteriores categorías, ejecutan acciones

suficientemente diferentes como para poder enmarcarlas en un solo tipo de instrucción. Por esta razón,

es necesario realizar más diagramas de tiempos de los que se podría esperar.

A continuación se muestran los diferentes diagramas de tiempos que cubren todas las instrucciones.

Para cada diagrama se especifica qué instrucciones cubre, aclaraciones que sea necesario realizar, y

solo se incluirán las señales que tienen importancia directa para la instrucción. Los nombres de las

señales usados son iguales que los que se encuentran en la descripción del sistema en bloques (ver

secciones 4.1, 9.1, 9.2).

9.3.1. Diagrama 1: Operaciones Aritméticas y Lógicas

El diagrama de tiempos mostrado en la Figura 41 incluye las siguientes instrucciones: Resta sin Signo;

Resta con Signo; Suma sin Signo; Suma con Signo; AND; OR; NOR. Dependiendo de la operación, la

señal SR_C_C estará activa durante el segundo evento, o no. Así mismo, dependiendo de la operación,

será el valor de los datos D_CARRY y D_OPERACION. En caso de que el registro de destino sea

R15, es necesario escoger como entrada el bus de entrada al banco de registros. El valor del dato

D_OP2 depende de si la instrucción es suma o resta. Para las operaciones lógicas, el valor de los datos

D_CARRY y D_OP2 no importa.

60

Figura 41. Diagrama de Tiempos para instrucciones USUB, SSUB, UADD, SADD, AND, OR, NOR.

9.3.2. Diagrama 2: Comparaciones

El diagrama de tiempos mostrado en la Figura 42 incluye las siguientes instrucciones: Comparación

con Signo; Comparación sin Signo. Dependiendo del resultado de la comparación, y del tipo de

comparación serán los valores que tenga el registro SR en sus Bits 0 y 1, así como el valor que tenga la

señal SEL_COMPARACION_C.

Figura 42. Diagrama de Tiempos para instrucciones: UCMP, SCMP.

9.3.3. Diagrama 3: Ramificaciones

El diagrama de tiempos mostrado en la Figura 43 incluye las siguientes instrucciones: Ramificación,

Menor qué; Ramificación, Igual qué.

Es importante aclarar que durante el primer ciclo de las instrucciones la Unidad de Control toma la

decisión de ejecutar la ramificación o no. Sin embargo, se aprovecha para realizar la suma entre los

operandos necesarios, con el fin de ahorrar tiempo.

61

Figura 43. Diagrama de Tiempos para instrucciones: JLT, JEQ.

9.3.4. Diagrama 4: Carga de Datos desde Memoria

El diagrama de tiempos mostrado en la Figura 44 incluye las siguientes instrucciones: Load. El cálculo

de la dirección se realiza por medio de la suma de los dos operandos. Los datos se tratan sin signo.

Además, se asume que la unidad de memoria reacciona en un tiempo igual o inferior a medio ciclo de

reloj.

62

Figura 44. Diagrama de Tiempos para instrucciones: LD.

9.3.5. Diagrama 5: Almacenamiento de Datos en Memoria

El diagrama de tiempos mostrado en la Figura 45 incluye las siguientes instrucciones: Store. El cálculo

de la dirección se realiza por medio de la suma de los dos operandos. Los datos se tratan sin signo.

Además, se asume que la unidad de memoria reacciona en un tiempo igual o inferior a medio ciclo de

reloj.

63

Figura 45. Diagrama de Tiempos para instrucciones: STR.

9.3.6. Diagrama 6: Carga de un Dato Inmediato

El diagrama de tiempos mostrado en la Figura 46 incluye las siguientes instrucciones: Load

Inmmediato16.

Figura 46. Diagrama de Tiempos para instrucciones: LDI.

9.3.7. Diagrama 7: Habilitación e Inhabilitación de Interrupciones

El diagrama de tiempos mostrado en la Figura 47 incluye las siguientes instrucciones: Habilitación de

Interrupciones; Inhabilitación de Interrupciones.

El valor que representa la señal D_IE dentro del diagrama corresponde a un 0 o 1 lógico, dependiendo

de si se está inhabilitando o habilitando la capacidad de atender interrupciones del núcleo.

64

Figura 47. Diagrama de Tiempos para instrucciones: IENA, IDIS.

9.3.8. Diagrama 8: Retorno desde Rutina de Interrupción

El diagrama de tiempos mostrado en la Figura 48 incluye las siguientes instrucciones: Retorno de

Interrupción.

Figura 48. Diagrama de Tiempos para instrucciones: RTI.

9.3.9. Diagrama 9: Atención a Solicitud de Interrupción

El diagrama de tiempos mostrado en la Figura 49 incluye las siguientes operaciones: Atención a

Interrupción. Los valores de N, Z y CT corresponden a lo que esté almacenado en el registro status

register en los Bits SR_OUT[0], SR_OUT[1] y SR_OUT[3] en el momento en que entra la

interrupción. El Bit SR_OUT[2] obligatoriamente debe tener el valor lógico „1‟, indicando que las

interrupciones están habilitadas. El Bit SR_OUT[4] inicialmente debe tener el valor lógico „0‟, pues

no se puede estar atendiendo ninguna interrupción antes de la que entra. Cuando se entra a la

excepción, se almacena el valor lógico „1‟ en él, indicando que se está dentro de una excepción.

65

Figura 49. Diagrama de Tiempos para operación: IRQ.

9.3.10. Diagrama 10: Excepciones

El diagrama de tiempos mostrado en la Figura 50 incluye las siguientes operaciones: Excepción por

ejecución de OpCode ilegal; Excepción por overflow aritmético; Excepción por retorno inválido de

interrupción. El valor del dato EXCP_SEL será “011”, “100” o “101”, dependiendo respectivamente

de la excepción. De igual forma, el valor del dato EXCP_VECTOR será 0x0004, 0x000F o 0x001A.

Los valores de N, Z, IE CT corresponden a lo que esté almacenado en el registro status register en los

Bits SR_OUT[0:3] en el momento en que entra la interrupción. No interesa si las interrupciones están

habilitadas o no, pues una excepción es una situación más grave y que el usuario debe tener siempre

en cuenta cuando ocurre. El Bit SR_OUT[4] inicialmente debe tener el valor lógico „0‟, pues no se

puede estar atendiendo ninguna interrupción antes de la que entra. Cuando se entra a la excepción, se

almacena el valor lógico „1‟ en él, indicando que se está dentro de una excepción.

66

Figura 50. Diagrama de Tiempos para operación: Excepción por OpCode ilegal, Excepción por overflow aritmético, Excepción por

salida inválida de interrupción.

9.3.11. Diagrama 11: Reintento de Búsqueda de Instrucción por Código

de Operación Ilegal

El diagrama de tiempos mostrado en la Figura 51 incluye las siguientes operaciones: Reintento de

búsqueda de instrucción por OpCode ilegal. El valor que tenga la señal ILEGAL_OP_DET_OUT

cuando haya un dato válido sobre el bus de datos indica a la Unidad de Control si el OpCode que se

intenta ejecutar es válido. Para el caso, si la señal tiene una valor lógico de „1‟, la instrucción no

existe, por tanto no se almacena la instrucción en el registro R_INST, ni se aumenta el valor del

registro R15. El valor de la señal ILEGAL_OP_DET_OUT tiene prioridad por encima de la señal

OPCODE_DET_OUT, por tanto el valor de esta última no interesa en este caso.

67

Figura 51. Diagrama de Tiempos para operación: Relectura por OpCode ilegal.

9.3.12. Diagrama 12: Fetch16

El diagrama de tiempos mostrado en la Figura 52 incluye las siguientes operaciones: Búsqueda de

instrucciones para instrucciones de 16 Bits. El valor que tenga la señal OPCODE_DET_OUT cuando

haya un dato válido sobre el bus de datos indica a la Unidad de Control si la instrucción es de 16 o 32

Bits. Para el caso, si la señal tiene un valor lógico de „0‟, la instrucción es de 16 Bits. Su valor el resto

del tiempo no interesa. El valor de la señal ILEGAL_OP_DET_OUT cuando haya un dato válido

sobre el bus de datos indica a la Unidad de Control si el OpCode que se intenta ejecutar es válido. Para

el caso, si la señal tiene un valor lógico de „0‟, la instrucción existe.

68

Figura 52. Diagrama de Tiempos para operación: FETCH16.

9.3.13. Diagrama 13: Fetch32

El diagrama de tiempos mostrado en la Figura 53 incluye las siguientes operaciones: Búsqueda de

instrucciones para instrucciones de 32 Bits. El valor que tenga la señal OPCODE_DET_OUT cuando

haya un dato válido sobre el bus de datos indica a la Unidad de Control si la instrucción es de 16 o 32

Bits. Para el caso, si la señal tiene un valor lógico de „1‟, la instrucción es de 32 Bits. Su valor el resto

del tiempo no interesa. El valor de la señal ILEGAL_OP_DET_OUT cuando haya un dato válido

sobre el bus de datos indica a la Unidad de Control si el OpCode que se intenta ejecutar es válido. Para

el caso, si la señal tiene un valor lógico de „0‟, la instrucción existe.

69

Figura 53. Diagrama de Tiempos para operación: FETCH32.

9.4. Anexo: Máquina de Estados De Unidad de Control

A continuación se muestra el diagrama de la máquina de estados que representa a la Unidad de

Control.

70

Figura 54. Máquina de Estados de la Unidad de Control.

La máquina de control consta de 40 pasos. Estos pasos incluyen las acciones de búsqueda de

instrucciones, decodificación, atención a interrupciones y excepciones y todas las 18 instrucciones del

núcleo. La unidad de control se diseño usando el método de diseño “one-hot” [9]. A continuación se

hace una descripción de los cambios en la máquina de control, para cada uno de los estados, por medio

del lenguaje AHPL. Se asume que todas las señales que no aparecen explícitamente en un paso

determinado tienen un valor lógico de „0‟.

S 0.

_ _ _ _ "11"; _ _ _ _ "10";

_ _ '1'; _ _ "000";

_ '1'; '1'; _ '0 ';

MUX BUS DIR IN C MUX R BANK IN C

MUX DEC C MUX PC C

READ ENA ENABLE W ENA

S1

S 1.

_ _ _ _ "11"; _ _ _ _ "10";

_ _ '1'; _ _ "000";

_ '0 '; '0 '; _ '1';

MUX BUS DIR IN C MUX R BANK IN C

MUX DEC C MUX PC C

READ ENA ENABLE W ENA

71

S2

S 2.

_ _ "001"; _ _ '1'; _ _ _ _ "00";

_ _ _ '0 '; _ _ '0 ';

_ _ _ '0 '; _ _ _ '0 ';

_ '1'; '1'; _ '0 ';

MUX PC C MUX DEC C MUX BUS DIR IN C

MUX SR IN C SR CT C

R INST C H R INST C L

READ ENA ENABLE W ENA

_ [2] _ _ _

_ [2] [1] [0] _ _ _

_ [2] [1] [0]

_ [2] [1] [0]

_ [2] [1] [0]

_ [3] _ _ _

_

S3

S3

S32

S32

S32

S37

S39

SR OUT ILEGAL OP DET OUT

SR OUT IRQ IRQ ILEGAL OP DET OUT

SR OUT IRQ IRQ

SR OUT IRQ IRQ

SR OUT IRQ IRQ

SR OUT ILEGAL OP DET OUT

SR [3] _ _ _OUT ILEGAL OP DET OUT

S 3.

_ _ "001"; _ _ '1'; _ _ _ _ "00";

_ _ _ '0 '; _ _ '1';

_ _ _ '1'; _ _ _ '0 ';

_ '0 '; '0 '; _ '1';

MUX PC C MUX DEC C MUX BUS DIR IN C

MUX SR IN C SR CT C

R INST C H R INST C L

READ ENA ENABLE W ENA

_ _

_ _ _ _ [31]

_ _ _ _ [31]

S4

S7

S9

OPCODE DET OUT

OPCODE DET OUT R INST OUT

OPCODE DET OUT R INST OUT

S 4.

_ _ "001"; _ _ '1'; _ _ _ _ "00";

_ _ _ '0 '; _ _ '0 ';

_ _ _ '0 '; _ _ _ '0 ';

_ '1'; '1'; _ '0 ';

MUX PC C MUX DEC C MUX BUS DIR IN C

MUX SR IN C SR CT C

R INST C H R INST C L

READ ENA ENABLE W ENA

S5

S 5.

_ _ "001"; _ _ '1'; _ _ _ _ "00";

_ _ _ '0 '; _ _ '0 ';

_ _ _ '0 '; _ _ _ '1';

_ '0 '; '0 '; _ '1';

MUX PC C MUX DEC C MUX BUS DIR IN C

MUX SR IN C SR CT C

R INST C H R INST C L

READ ENA ENABLE W ENA

_ _ [31]

_ _ [31]

S6

S8

R INST OUT

R INST OUT

72

S 6.

_ _ [29] _ _ [28] _ _ [27]

_ _ [29] _ _ [28] _ _ [27]

_ _ [29] _ _ [28] _ _ [27]

_ _ [29] _ _ [28] _ _ [27]

S10

S11

S12

S13

S14

R INST OUT R INST OUT R INST OUT

R INST OUT R INST OUT R INST OUT

R INST OUT R INST OUT R INST OUT

R INST OUT R INST OUT R INST OUT

_ _ [29] _ _ [28] _ _ [27]

_ _ [29] _ _ [28] _ _ [27]

_ _ [29] _ _ [28] _ _ [27]

S15

S16

R INST OUT R INST OUT R INST OUT

R INST OUT R INST OUT R INST OUT

R INST OUT R INST OUT R INST OUT

S 7.

_ _ [29] _ _ [28] _ _ [27]

_ _ [29] _ _ [28] _ _ [27]

_ _ [29] _ _ [28] _ _ [27]

_ _ [29] _ _ [28] _ _ [27]

S2

S18

S19

S20

S21

R INST OUT R INST OUT R INST OUT

R INST OUT R INST OUT R INST OUT

R INST OUT R INST OUT R INST OUT

R INST OUT R INST OUT R INST OUT

_ _ [29] _ _ [28] _ _ [27]

_ _ [29] _ _ [28] _ _ [27] _ [4]

_ _ [29] _ _ [28] _ _ [27] _ [4]

S22

S35

R INST OUT R INST OUT R INST OUT

R INST OUT R INST OUT R INST OUT SR OUT

R INST OUT R INST OUT R INST OUT SR OUT

S 8.

_ _ [29] _ _ [28] _ _ [27]

_ _ [29] _ _ [28] _ _ [27]

_ _ [29] _ _ [28] _ _ [27]

S23

S26

S28

R INST OUT R INST OUT R INST OUT

R INST OUT R INST OUT R INST OUT

R INST OUT R INST OUT R INST OUT

S 9.

_ _ [29] _ _ [28] _ _ [27]

_ _ [29] _ _ [28] _ _ [27]

S29

S30

R INST OUT R INST OUT R INST OUT

R INST OUT R INST OUT R INST OUT

S 10.

1_ _ '0 '; _ _ '0 '; _ 2 _ '0 ';

_ _ _ "00";

_ _ _ _ "00"; _ _ "000"; _ _ '0 ';

_ _ '1'; _ '0 ';

OP SOURCE C SEL CARRY C SEL OP C

ALU SEL OUT C

MUX R BANK IN C MUX PC C MUX DEC C

R ALU C W ENA

S17

S 11.

73

1_ _ '0 '; _ _ '0 '; _ 2 _ '0 ';

_ _ _ "00";

_ _ _ _ "00"; _ _ "000"; _ _ '0 ';

_ _ '1'; _ '0 ';

OP SOURCE C SEL CARRY C SEL OP C

ALU SEL OUT C

MUX R BANK IN C MUX PC C MUX DEC C

R ALU C W ENA

S17

S36

V

V

S 12.

1_ _ '0 '; _ _ '1'; _ 2 _ '1';

_ _ _ "00";

_ _ _ _ "00"; _ _ "000"; _ _ '0 ';

_ _ '1'; _ '0 ';

OP SOURCE C SEL CARRY C SEL OP C

ALU SEL OUT C

MUX R BANK IN C MUX PC C MUX DEC C

R ALU C W ENA

S17

S 13.

1_ _ '0 '; _ _ '1'; _ 2 _ '1';

_ _ _ "00";

_ _ _ _ "00"; _ _ "000"; _ _ '0 ';

_ _ '1'; _ '0 ';

OP SOURCE C SEL CARRY C SEL OP C

ALU SEL OUT C

MUX R BANK IN C MUX PC C MUX DEC C

R ALU C W ENA

S17

S36

V

V

S 14.

1_ _ '0 '; _ _ '0 '; _ 2 _ '0 ';

_ _ _ "01";

_ _ _ _ "00"; _ _ "000"; _ _ '0 ';

_ _ '1'; _ '0 ';

OP SOURCE C SEL CARRY C SEL OP C

ALU SEL OUT C

MUX R BANK IN C MUX PC C MUX DEC C

R ALU C W ENA

S17

S 15.

1_ _ '0 '; _ _ '0 '; _ 2 _ '0 ';

_ _ _ "10";

_ _ _ _ "00"; _ _ "000"; _ _ '0 ';

_ _ '1'; _ '0 ';

OP SOURCE C SEL CARRY C SEL OP C

ALU SEL OUT C

MUX R BANK IN C MUX PC C MUX DEC C

R ALU C W ENA

S17

S 16

74

1_ _ '0 '; _ _ '0 '; _ 2 _ '0 ';

_ _ _ "11";

_ _ _ _ "00"; _ _ "000"; _ _ '0 ';

_ _ '1'; _ '0 ';

OP SOURCE C SEL CARRY C SEL OP C

ALU SEL OUT C

MUX R BANK IN C MUX PC C MUX DEC C

R ALU C W ENA

S17

S 17.

1_ _ '0 '; _ _ '0 '; _ 2 _ '0 ';

_ _ _ "00";

_ _ _ _ "00"; _ _ "000"; _ _ '0 ';

_ _ '0 '; _ '1';

OP SOURCE C SEL CARRY C SEL OP C

ALU SEL OUT C

MUX R BANK IN C MUX PC C MUX DEC C

R ALU C W ENA

S2

S 18.

1_ _ '0 '; _ _ '1'; _ 2 _ '1';

_ _ '0 '; _ _ _ '0 ';

_ _ '1'; _ _ '1';

OP SOURCE C SEL CARRY C SEL OP C

SEL COMPARACION C MUX SR IN C

SR N C SR Z C

S2

S 19.

1_ _ '0 '; _ _ '1'; _ 2 _ '1';

_ _ '1'; _ _ _ '0 ';

_ _ '1'; _ _ '1';

OP SOURCE C SEL CARRY C SEL OP C

SEL COMPARACION C MUX SR IN C

SR N C SR Z C

S2

S 20.

_ _ _ '0'; _ '0'; _ _ '1';MUX SR IN C IE IN SR IE C

S2

S 21.

_ _ _ '0'; _ '1'; _ _ '1';MUX SR IN C IE IN SR IE C

S2

S 22.

_ _ "010"; _ _ '1'; _ _ _ '1';

_ _ '1'; _ _ '1'; _ _ '1'; _ _ '1'; _ _ '1';

_ '1';

MUX PC C MUX DEC C MUX SR IN C

SR N C SR Z C SR IE C SR CT C SR IA C

W ENA

S2

75

S 23.

1_ _ '0 '; _ 2 _ '0 '; _ _ '0 ';

_ _ _ "00";

_ _ _ _ "01"; _ _ _ _ "10"; _ _ '0 ';

_ _ "000";

_ _ '1'; _ '0 '; '0 '; _ '0 ';

OP SOURCE C SEL OP C SEL CARRY C

ALU SEL OUT C

MUX BUS DIR IN C MUX R BANK IN C MUX DEC C

MUX PC C

R ALU C READ ENA ENABLE W ENA

S24

S 24.

1_ _ '0 '; _ 2 _ '0 '; _ _ '0 ';

_ _ _ "00";

_ _ _ _ "01"; _ _ _ _ "10"; _ _ '0 ';

_ _ "000";

_ _ '0 '; _ '1'; '1'; _ '0 ';

OP SOURCE C SEL OP C SEL CARRY C

ALU SEL OUT C

MUX BUS DIR IN C MUX R BANK IN C MUX DEC C

MUX PC C

R ALU C READ ENA ENABLE W ENA

S25

S 25.

1_ _ '0 '; _ 2 _ '0 '; _ _ '0 ';

_ _ _ "00";

_ _ _ _ "01"; _ _ _ _ "10"; _ _ '0 ';

_ _ "000";

_ _ '0 '; _ '0 '; '0 '; _ '1';

OP SOURCE C SEL OP C SEL CARRY C

ALU SEL OUT C

MUX BUS DIR IN C MUX R BANK IN C MUX DEC C

MUX PC C

R ALU C READ ENA ENABLE W ENA

S2

S 26.

1_ _ '0'; _ 2 _ '0'; _ _ '0 ';

_ _ _ "00"; _ _ _ _ "01";

_ _ '1'; _ '0 '; '0 ';

OP SOURCE C SEL OP C SEL CARRY C

ALU SEL OUT C MUX BUS DIR IN C

R ALU C WRITE ENA ENABLE

S27

S 27.

1_ _ '1'; _ 2 _ '0'; _ _ '0 ';

_ _ _ "00"; _ _ _ _ "01";

_ _ '0 '; _ '1'; '1';

OP SOURCE C SEL OP C SEL CARRY C

ALU SEL OUT C MUX BUS DIR IN C

R ALU C WRITE ENA ENABLE

S2

S 28.

_ _ _ _ "01"; _ _ '0 '; _ _ "000";

_ '1';

MUX R BANK IN C MUX DEC C MUX PC C

W ENA

76

S2

S 29.

1_ _ '0 '; _ 2 _ '0 '; _ _ '0 ';

_ _ _ "00";

_ _ '1'; _ _ _ _ "00"; _ _ "000";

_ _ '1'; _ '0 ';

OP SOURCE C SEL OP C SEL CARRY C

ALU SEL OUT C

MUX DEC C MUX R BANK IN C MUX PC C

R ALU C W ENA

_ [0]

_ [0]

S31

S2

SR OUT

SR OUT

S 30.

1_ _ '0 '; _ 2 _ '0 '; _ _ '0 ';

_ _ _ "00";

_ _ '1'; _ _ _ _ "00"; _ _ "000";

_ _ '1'; _ '0 ';

OP SOURCE C SEL OP C SEL CARRY C

ALU SEL OUT C

MUX DEC C MUX R BANK IN C MUX PC C

R ALU C W ENA

_ [1]

_ [1]

S31

S2

SR OUT

SR OUT

S 31.

1_ _ '0 '; _ 2 _ '0 '; _ _ '0 ';

_ _ _ "00";

_ _ '1'; _ _ _ _ "00"; _ _ "000";

_ _ '0 '; _ '1';

OP SOURCE C SEL OP C SEL CARRY C

ALU SEL OUT C

MUX DEC C MUX R BANK IN C MUX PC C

R ALU C W ENA

S2

S 32.

_ _ "000"; _ _ '1';

_ _ _ _ "10"; _ _ _ _ "10";

_ _ _ '0 '; _ '1';

_ _ '1'; _ _ '1'; _ _ '1'; _ _ '1';

_ '0 '; '0 '; _ '0 '; _ '0 ';

MUX PC C MUX DEC C

MUX BUS DIR IN C MUX R BANK IN C

MUX SR IN C IA IN

PC C C SR C C R IRQ C SR IA C

READ ENABLE ENABLE W ENA SR RST

S33

S 33.

_ _ "000"; _ _ '1';

_ _ _ _ "10"; _ _ _ _ "10";

_ _ _ '0 '; _ '1';

_ _ '0 '; _ _ '0 '; _ _ '0 '; _ _ '0 ';

_ '1'; '1'; _ '0 '; _ '1';

MUX PC C MUX DEC C

MUX BUS DIR IN C MUX R BANK IN C

MUX SR IN C IA IN

PC C C SR C C R IRQ C SR IA C

READ ENABLE ENABLE W ENA SR RST

77

S34

S 34.

_ _ "000"; _ _ '1';

_ _ _ _ "10"; _ _ _ _ "10";

_ _ _ '0 '; _ '0 ';

_ _ '0 '; _ _ '0 '; _ _ '0 '; _ _ '0 ';

_ '0 '; '0 '; _ '1'; _ '0 ';

MUX PC C MUX DEC C

MUX BUS DIR IN C MUX R BANK IN C

MUX SR IN C IA IN

PC C C SR C C R IRQ C SR IA C

READ ENABLE ENABLE W ENA SR RST

S2

S 35.

_ _ '1'; _ _ "101"; _ _ _ '0'; _ '1';

_ _ '1'; _ _ '1'; _ _ '1';

_ '1'; _ '0 ';

MUX DEC C MUX PC C MUX SR IN C IA IN

PC C C SR C C SR IA C

W ENA SR RST

S38

S 36.

_ _ '1'; _ _ "100"; _ _ _ '0'; _ '1';

_ _ '1'; _ _ '1'; _ _ '1';

_ '1'; _ '0 ';

MUX DEC C MUX PC C MUX SR IN C IA IN

PC C C SR C C SR IA C

W ENA SR RST

S38

S 37.

_ _ '1'; _ _ "011"; _ _ _ '0'; _ '1';

_ _ '1'; _ _ '1'; _ _ '1';

_ '1'; _ '0 ';

MUX DEC C MUX PC C MUX SR IN C IA IN

PC C C SR C C SR IA C

W ENA SR RST

S38

S 38.

_ _ '1'; _ _ "000"; _ _ _ '0'; _ '0 ';

_ _ '0 '; _ _ '0 '; _ _ '0 ';

_ '0 '; _ '1';

MUX DEC C MUX PC C MUX SR IN C IA IN

PC C C SR C C SR IA C

W ENA SR RST

S2

S 39.

_ _ "001"; _ _ '1'; _ _ _ _ "00";

_ _ _ '0 '; _ _ '1'; _ '1';

_ _ _ '0 '; _ _ _ '0 ';

_ '0 '; '0 '; _ '0 ';

MUX PC C MUX DEC C MUX BUS DIR IN C

MUX SR IN C SR CT C CT IN

R INST C H R INST C L

READ ENA ENABLE W ENA

78

S2

9.5. Anexo: Protocolo de Pruebas

9.5.1. Esquema del Protocolo

Para poder determinar las pruebas que es necesario realizar, se debe establecer qué debe cumplir el

sistema para ser considerado como correcto. De esta forma, es posible enrutar las pruebas hacia el

cumplimiento de esas especificaciones.

El sistema tiene varias partes que deben ser revisadas y analizadas correctamente. Inicialmente, es

necesario poder determinar que los sistemas funcionan de acuerdo a su diseño. Para lograr esto, se

usará la herramienta de desarrollo Quartus II.

9.5.1.1. Prueba de Descripción de Hardware e Interconexiones.

Cuando se tenga completo el diseño del sistema, se hace su descripción por medio del lenguaje

VHDL, en la herramienta mencionada. Inicialmente, se describe cada uno de los componentes básicos

del sistema por separado: tales como Flip Flops, y bloques de lógica. Posterior a su descripción, se

realizan simulaciones de cada bloque, para confirmar que funcionan de acuerdo a lo que se espera. Los

parámetros que indican el correcto funcionamiento de cada componente dependen del componente

específico. Por ejemplo, se espera que una compuerta lógica cumpla con su ecuación combinatoria de

forma correcta.

Con estos componentes básicos han sido descritos y su funcionamiento comprobado por medio de

simulación, se empieza a hacer la integración de estos componentes para la formación de los bloques

más básicos del sistema, tales como registros y multiplexores. Estos bloques siguen el mismo proceso

que los componentes básicos con los cuales se construyen: descripción en VHDL basada en los

componentes básicos cuyo funcionamiento ha sido comprobado por simulación, simulación de los

bloques y correcciones necesarias para asegurar su correcto funcionamiento.

El proceso de integración y simulación paso a paso, anteriormente descrito, se sigue realizando hasta

llegar a integrar todo el sistema. Una vez hecho esto, se comprueba su funcionamiento por medio de

simulación. Puesto que la unidad de datos no está conectada a la memoria ni a la unidad de control, es

necesario proveer estas señales a la misma. Esto se hace, paso a paso con cada una de todas las

posibles operaciones que la unidad de datos es capaz de realizar. Se monitorean los buses de datos y

de direcciones para comprobar que la información está siguiendo los caminos correctos a través de la

unidad de datos.

Una vez que esto ha sido comprobado por simulación, se describe la unidad de control, por medio del

lenguaje AHPL. Una vez que todas las ecuaciones lógicas para transición de estados han sido

caracterizadas, se hace la descripción de su comportamiento en VHDL. Para verificar su

funcionamiento por simulación, se introducen las señales que ésta espera en sus entradas, y se verifica

que ésta pase por todos los estados necesarios de acuerdo a esa información. Finalmente, se hace la

integración de ambas unidades. De esta manera, es posible empezar a introducir instrucciones al

sistema, de forma manual y controlada, para verificar luego los resultados de las mismas, y comprobar

que el sistema funciona correctamente.

En caso de que una instrucción no realice las operaciones que se esperan, se verifica la descripción de

todo el sistema. Es importante identificar y verificar interconexiones entre bloques y entre las dos

unidades, pues es más probable que se cometan errores en estas secciones de la descripción que en los

bloques como tal, dado que los bloques, en este momento de la integración, han sido comprobados de

manera independiente en su funcionamiento. Si la descripción es correcta, se requiere verificar el

diseño de la o las instrucciones y operaciones que están generando inconvenientes, y de ser necesario,

se rediseñan para que cumplan con las especificaciones.

79

Cuando todo el sistema integrado ha sido comprobado, se hace una última simulación, conectando el

sistema a la memoria interna del chip FPGA. Sobre esta memoria se escribe un programa que haga uso

de la mayor cantidad de recursos del sistema.

La compilación de todo el sistema indicará la máxima velocidad a la que puede trabajar el reloj, de

forma que los tiempos de hold y setup no afecten la funcionalidad de las operaciones del sistema. Se

espera que el sistema pueda funcionara con la velocidad objetivo mínima de 50MHz. En caso de que

el sistema no logre funcionar a esta velocidad de reloj, se utiliza la herramienta de análisis de ruta

crítica del software Quartus II, para determinar donde se encuentra el lugar o lugares físicos del chip

FPGA que impiden su operación a esta velocidad. Después de esto, se procede a la optimización de

dichas rutas, para determinar si es posible lograr la velocidad de reloj deseada.

Cuando todas las pruebas de simulación hayan sido ejecutadas de forma satisfactoria de acuerdo al

diseño del sistema, es necesario comprobar su funcionamiento real. Para esto, se programa el chip

FPGA con el sistema simulado en correcto funcionamiento, programado a través de una tarjeta de

desarrollo y el software Quartus II. Para poder determinar que el sistema real está funcionando

correctamente, se debe hacer uso de un analizador de estados lógicos que permita revisar los buses del

sistema, así como el estado de los registros internos. Para lograr esto, se requiere enrutar algunas

señales hacia terminales externos del FPGA, a donde se pueda conectar el analizador. El programa que

se usa para la comprobación sobre hardware es el mismo usado en simulación. Esto se hace con el fin

de poder comprobar que el hardware y la simulación entregan resultados iguales. Dada la cantidad de

depuración que el sistema ha tenido hasta este momento, se espera que funcione adecuadamente el

hardware de igual forma a como lo hace en simulación. En caso de no ser así, es necesario verificar

dónde existen diferencias y hacer la corrección de errores que sea necesaria.

9.5.1.2. Prueba del Sistema por medio de un Programa.

El programa que se encargará de demostrar el funcionamiento del sistema está basado en el desarrollo

de la Serie de Fibonacci. Esta serie indica que:

F0 = 0. F1 = 1.

Fn = Fn-2 + Fn-1, n>1, n natural.

Se empezará el desarrollo de la serie, hasta el momento en que la unidad aritmética del sistema genere

una excepción por overflow aritmético. En la rutina de excepción, se reinicia la serie desde cero.

Además del cálculo de la serie, hay un registro que se encarga de aumentar en 1 su valor, una vez que

se genera un cálculo nuevo.

Se implementará un contador interno en el sistema. Este contador se conectará a una de las líneas de

interrupción del núcleo. El contador contará con una señal de reinicio, conectada al bus de direcciones

del sistema. Este esquema permite interrumpir al sistema cada cierto tiempo. En la rutina de

interrupción se reinicia el valor del contador hasta cero, y se modifica otra bandera. De esta manera,

cuando la rutina principal revise esta segunda bandera y vea que está activa, copiará el registro que

guarda el valor que guarda el número de cálculo realizado en memoria. Luego de la copia, borra la

bandera.

El lugar en el cual se almacenan los conteos de la serie es en una lista circular. Para la lista se tiene un

valor de dirección que corresponde a la cabeza y otro que corresponde a la cola, además de un

apuntador que está siempre apuntando al lugar de la lista donde se escribirá el siguiente valor. Cuando

dicho apuntador llegue a ser igual a la dirección de la cola de la lista, será reiniciado con el valor de la

cabeza.

Además de todo lo anterior, siempre que se reinicie el cálculo de la serie desde cero, se leerán desde

memoria los últimos dos datos almacenados en la lista que almacena los conteos. Estos dos datos serán

comparados para saber cual es el mayor de ambos y restar de este el menor. El resultado de esta resta

será la cantidad de elementos de la serie que han sido calculados en el tiempo que ocurren dos

80

interrupciones consecutivas. Sabiendo esto, y además cuántas instrucciones se ejecutan en el cálculo

de un solo elemento de la serie, es posible obtener un dato estimado de cuantas instrucciones ejecuta el

núcleo por segundo (sabiendo el tiempo que transcurre entre una interrupción y la siguiente, que es

igual a un tiempo específico indicado por el contador conectado a la línea de interrupciones).

A continuación se muestran los diagramas de flujo del programa que se implementará para probar el

funcionamiento del núcleo.

81

Figura 55. Diagrama de Flujo de la rutina principal del programa de prueba.

Figura 56. Diagrama de Flujo de la sub-rutina de almacenamiento de datos en las listas en memoria.

82

Figura 57. Diagrama de Flujo de la sub-rutina de carga de datos desde la lista 2, que almacena el subíndice de la serie.

Figura 58. Diagrama de Flujo de la rutina de atención a Interrupción por el contador externo.

83

Figura 59. Diagrama de Flujo de atención a Excepción por overflow aritmético.

9.5.2. Codigo Fuente de Programa de Prueba

El código en lenguaje pseudoensamblador para el programa de prueba se encuentra implementado en

el siguiente archivo de texto, en el disco adjunto:

o root:\Protocolo de Pruebas\Programa de Prueba\Protocolo.txt

Para su visualización, se recomienda abrirlo con el editor de texto de Windows, con la ventana

maximizada, y quitando la opción “Ajuste de Línea” del menú “Formato”.

9.6. Anexo: Simulaciones

Para poder determinar el correcto funcionamiento del diseño, es necesario realizar pruebas sobre este.

Las pruebas no solo se limitan a su realización en el momento de su implementación en hardware. Es

necesario poder validar que el diseño es correcto antes de su implementación. Para esto, es necesario

realizar simulaciones del diseño. Esto permite la corrección de errores del sistema, así como su

depuración, reduciendo el tiempo gastado en estas tareas sobre el hardware en sí.

9.6.1. Núcleo

9.6.1.1. Simulación 1.

La primera simulación realizada se puede reproducir abriendo el proyecto que representa al núcleo,

por medio de los siguientes pasos:

Abrir el proyecto en el archivo que se encuentra en el disco adjunto:

o root:\VHDL\NUCLEO\NUCLEO.qpf

Compilar el proyecto.

En el menú Assignments Settings… Simulator Settings, escoger como archivo de

simulación NUCLEO_Sim1.vwf.

Correr la simulación.

Los pasos que se realizan en esta simulación son los siguientes:

1. Se cargan los datos 0x0001 a 0x000E en los registros R1 a R14 respectivamente, usando LDI.

84

2. Se almacenan en memoria los datos que hay en los registros R0 a R15, en la dirección R0+R0,

R1+R1, y así sucesivamente hasta R15+R15. Se usa STR.

3. Se realiza la suma UADD R1 = R2 + R10.

4. Se realiza la suma SADD R2 = R3 + R11.

5. Se realiza la resta USUB R3 = R4 – R12.

6. Se realiza la resta SSUB R4 = R5 – R13.

7. Se realiza la operación lógica AND R5 = R6 and R14.

8. Se realiza la operación lógica OR R6 = R7 or R15.

9. Se realiza la operación lógica NOR R7 = R8 nor R0.

10. Se carga desde memoria, en la dirección R11+R3, un dato, y se guarda en el registro R8.

11. Se ejecuta un NOP.

Esto finaliza la simulación del archivo NUCLEO-Sim1.vwf.

9.6.1.2. Simulación 2.

La segunda simulación realizada se puede reproducir abriendo el proyecto que representa al núcleo,

por medio de los siguientes pasos:

Abrir el proyecto en el archivo que se encuentra en el disco adjunto:

o root:\VHDL\NUCLEO\NUCLEO.qpf

Compilar el proyecto.

En el menú Assignments Settings… Simulator Settings, escoger como archivo de

simulación NUCLEO_Sim2.vwf.

Correr la simulación.

Los pasos que se realizan en esta simulación son los siguientes:

1. Se compara, sin signo, a R1 con R0, y se intenta un salto condicional tipo JLT. El salto no ocurre,

como se espera, pues R1 es mayor a R0, con datos sin signo.

2. Se compara, sin signo, a R0 con R1, y se intenta un salto condicional tipo JLT. El salto ocurre,

como se espera, pues R0 es menor que R1, con datos sin signo.

3. Se compara, con signo, a R0 con R1, y se intenta un salto condicional tipo JLT. El salto no ocurre,

como se espera, pues R0 es mayor a R1, cuando los datos se toman como con signo en

complemento a 2 (0x8000 es igual a -32768, que es menor que 0).

4. Se compara, con signo, a R1 con R0, y se intenta un salto condicional tipo JLT. El salto ocurre,

como se espera, pues R1 es menor que R0, con datos con signo en complemento a 2.

5. Se compara, sin signo, a R1 con R2, y se intenta un salto condicional tipo JEQ. El salto ocurre,

como se espera, pues R1 y R2 son iguales.

6. Se compara, sin signo, a R1 con R0, y se intenta un salto condicional tipo JEQ. El salto no ocurre,

como se espera, pues R1 y R0 no son iguales.

85

7. Se compara, con signo, a R1 con R2, y se intenta un salto condicional tipo JEQ. El salto ocurre,

como se espera, pues R1 y R2 son iguales.

8. Se compara, con signo, a R1 con R0, y se3 intenta un salto condicional tipo JEQ. El salto no

ocurre, como se espera, pues R1 y R0 no son iguales.

Esto finaliza la simulación del archivo NUCLEO-Sim2.vwf.

9.6.1.3. Simulación 3.

La tercera simulación realizada se puede reproducir abriendo el proyecto que representa al núcleo, por

medio de los siguientes pasos:

Abrir el proyecto en el archivo que se encuentra en el disco adjunto:

o root:\VHDL\NUCLEO\NUCLEO.qpf

Compilar el proyecto.

En el menú Assignments Settings… Simulator Settings, escoger como archivo de

simulación NUCLEO_Sim3.vwf.

Correr la simulación.

Los pasos que se realizan en esta simulación son los siguientes:

1. Se ejecuta la instrucción de habilitación de interrupciones IENA.

2. Se solicita interrupción del sistema por la primera interrupción. El sistema responde cargando el

vector de interrupción que, por diseño corresponde a dicha interrupción.(REFERENCIA A

CONJUNTO DE INSTRUCCIONES)

3. Se termina la rutina de interrupción con la instrucción RTI. Se comprueba que el contador de

programa continúa en la instrucción previa a la atención a interrupción.

4. Se solicita interrupción del sistema por la segunda interrupción. El sistema responde cargando el

vector de interrupción que, por diseño corresponde a dicha interrupción.

5. Se termina la rutina de interrupción con la instrucción RTI. Se comprueba que el contador de

programa continúa en la instrucción previa a la atención a interrupción.

6. Se solicita interrupción del sistema por la tercera interrupción. El sistema responde cargando el

vector de interrupción que, por diseño corresponde a dicha interrupción.

7. Se termina la rutina de interrupción con la instrucción RTI. Se comprueba que el contador de

programa continúa en la instrucción previa a la atención a interrupción.

8. Se ejecuta la instrucción de inhabilitación de interrupciones IDIS.

9. Se solicita interrupción del sistema por la primera interrupción. El sistema responde ignorando la

interrupción y continúa con la ejecución normal del programa.

10. Se solicita interrupción del sistema por la segunda interrupción. El sistema responde ignorando la

interrupción y continúa con la ejecución normal del programa.

11. Se solicita interrupción del sistema por la tercera interrupción. El sistema responde ignorando la

interrupción y continúa con la ejecución normal del programa.

Esto finaliza la simulación del archivo NUCLEO-Sim3.vwf.

86

9.6.1.4. Simulación 4.

La cuarta simulación realizada se puede reproducir abriendo el proyecto que representa al núcleo, por

medio de los siguientes pasos:

Abrir el proyecto en el archivo que se encuentra en le archivo adjunto:

o root:\VHDL\NUCLEO\NUCLEO.qpf

Compilar el proyecto.

En el menú Assignments Settings… Simulator Settings, escoger como archivo de

simulación NUCLEO_Sim4.vwf.

Correr la simulación.

Los pasos que se realizan en esta simulación son los siguientes:

1. Se carga el dato 0x7FFF al registro R1, usando LDI.

2. Se carga el dato 0x8001 al registro R2, usando LDI.

3. Se realiza la suma SADD R1 = R1+R1. Esto genera overflow aritmético, y el núcleo desvía su

ejecución a la dirección de inicio de la rutina de atención a una excepción por overflow aritmético.

4. Se termina la rutina de atención a excepción, usando la instrucción RTI.

5. Se realiza la resta SSUB R2 = R2-R1. Esto genera overflow aritmético, y el núcleo desvía su

ejecución a la dirección de inicio de la rutina de atención a una excepción por overflow aritmético.

6. Se termina la rutina de atención a excepción, usando la instrucción RTI.

7. Se ejecuta una vez mas RTI para comprobar la excepción por uso ilegal de la instrucción (tratar de

salir de una interrupción o excepción cuando no se está dentro de una). El núcleo desvía la

ejecución del programa hacia la rutina de atención a la excepción por uso ilegal de la instrucción

RTI.

8. Se termina la rutina de atención a excepción, usando la instrucción RTI.

9. Se introduce una instrucción no definida, 0x3800, usando el código de operación no definido

0x38.

10. El núcleo reconoce el código de operación no definido y reintenta la lectura de esa posición de

memoria. El código indefinido es reintroducido al núcleo.

11. El núcleo reconoce el código de operación no definido, y teniendo en cuenta que ya intentó la

relectura, desvía la ejecución del programa hacia la rutina de atención a la excepción por código

de operación no válido.

12. Se termina la rutina de atención a excepción, usando la instrucción RTI.

Esto finaliza la simulación del archivo NUCLEO-Sim4.vwf.

9.6.2. Sistema

La simulación del sistema con memorias y un periférico externo se puede reproducir abriendo el

proyecto que representa al sistema bajo esta condición de conexión, por medio de los siguientes pasos:

Abrir el proyecto en el archivo:

87

o root:\VHDL\SISTEMA\SISTEMA.qpf

Compilar el proyecto.

En el menú Assignments Settings… Simulator Settings, escoger como archivo de

simulación Sistema_SIM.vwf.

Correr la simulación.

A continuación se realiza una explicación, en términos de operaciones, de la simulación, usando como

referencia los tiempos en que ocurren las operaciones. Lo más destacado se explicará. Es posible

determinar exactamente qué instrucción se está ejecutando, revisando la señal del bus BUS_DIR, la

cual tiene, la mayoría del tiempo (cuando no se está leyendo o escribiendo sobre la memoria RAM o el

contador síncrono) la dirección almacenada en el Program Counter. Es necesario leer el protocolo de

pruebas, presente en el Anexo ANEXO-PROTOCOLO DE PRUEBAS para entender la simulación.

La simulación se ejecuta con una frecuencia de reloj de 50MHz, con el fin de poder compararla con

los resultados del sistema en tiempo real, sobre la FPGA.

0.1us – 0.5us. Secuencia de inicio del sistema. Se carga el vector de inicio del programa.

0.5us – 9.7us. Inicialización de registros, habilitación de interrupciones.

9,7us – 15.1us. Revisión de interrupción por contador externo.

15.1us – 16.3us. No hubo interrupción. Salto a cálculo del siguiente valor de la serie.

16.3us – 20.5us. Se calcula el siguiente valor de la serie.

20.5us – 21.7us. Salto a aumento de la cantidad de cálculos realizados.

21.7us – 24.3us. Aumenta de la cantidad de cálculos realizados.

24.3us – 27.1us. Revisión del último valor calculado de la serie.

27.1us – 28.3us. Reiniciación de la rutina principal.

28.3us – 102.4us. Repetición de la rutina principal hasta ocurrencia de interrupción por contador

externo.

102.9us – 103.5us. Carga de vector de interrupción para interrupción 1.

103.5us – 109.5us. Ejecución de rutina de atención por interrupción 1, y retorno desde

interrupción.

109.5us – 115.1us. Ejecución de rutina principal. Revisión de ocurrencia de interrupción. Salto a

rutina de almacenamiento de datos en RAM.

115.1us – 128.1us. Ejecución de rutina de almacenamiento de datos en RAM. Retorno a rutina

principal.

128.1us – 211.0us. Repetición de la rutina principal hasta ocurrencia de interrupción por contador

externo.

211.5us – 218.1us. Ejecución de rutina de atención a interrupción 1 y retorno.

218.1us – 501.9us. Ejecución del ciclo principal, con interrupciones y rutinas secundarias, hasta

que ocurre excepción por overflow aritmético.

88

501.9us – 502.3us. Carga del vector de excepción para overflow aritmético.

502.3us – 505.5us. Ejecución de rutina de atención a excepción por overflow aritmético, y retorno

a rutina principal.

515.1us. Salto a rutina de carga de datos desde memoria RAM.

515.1us – 537.2us. Ejecución de rutina de carga de datos desde memoria RAM, hasta ocurrencia

de interrupción por contador externo.

537.2us – 544.3us. Ejecución de rutina de atención a interrupción 1 y retorno.

544.3us – 546.9us. Ejecución del resto de la rutina de carga de datos desde memoria RAM y

retorno a rutina principal.

546.9us – 646.5us. Ejecución de rutina principal hasta ocurrencia de interrupción por contador

externo.

646.5us – 653.3us. Ejecución de rutina de atención a interrupción 1 y retorno.

653.3us – 716.7us, fin de simulación. Ejecución del programa repite el patrón mostrado hasta el

momento.

Esto finaliza la simulación del archivo Sistema_SIM.vwf.

9.7. Anexo: Prueba en FPGA del Sistema

El resultado de la ejecución del programa de prueba sobre el sistema con memorias y un periférico

externo, sobre la FPGA en tiempo real, se puede observar abriendo el archivo de simulación que se

indica a continuación, por medio de los siguientes pasos:

Abrir el archivo de simulación que se encuentra en el disco adjunto:

o root:\VHDL\SISTEMA\Sistema_HW.vwf

El resultado de simulación ha sido consignado en un archivo de simulación de Quartus II, para una

visualización sencilla del mismo.

Como se puede ver, la captura de señales por parte del analizador lógico solo ocurre durante el borde

de subida del reloj CLK, por lo cual algunas señales aparecen corridas medio ciclo de reloj hacia el

futuro, comparadas con las señales que se observan en la simulación del sistema (ver Anexo ANEXO-

SIMULACION-SISTEMA).

A continuación se realiza una explicación, en términos de operaciones, de la simulación, usando como

referencia los tiempos en que ocurren las operaciones. Lo más destacado se explicará. Además de esto,

es posible observar los resultados de los cálculos realizados, en los registros internos.

0.1us – 0.5us. Secuencia de inicio del sistema. Se carga el vector de inicio del programa.

0.5us – 9.7us. Inicialización de registros, habilitación de interrupciones. Se observa que R1 =

0x0000, R2 = 0x0001, R4 = 0x0000, R7 = 0x0200, R8 = 0x02FF, R10 = 0x0200, R13 = 0x0000.

9,7us – 15.1us. Revisión de interrupción por contador externo. Se usa R5 = 0x005C como vector

de salto condicional.

15.1us – 16.3us. No hubo interrupción. Salto a cálculo del siguiente valor de la serie.

89

16.3us – 20.5us. Se calcula el siguiente valor de la serie. Se observa que R3 = 0x0001, R2 =

0x0001, R1 = 0x0001.

20.5us – 21.7us. Salto a aumento de la cantidad de cálculos realizados.

21.7us – 24.3us. Aumenta de la cantidad de cálculos realizados. Se observa que R4 = 0x0001.

24.3us – 27.1us. Revisión del último valor calculado de la serie. Se usa R5 = 0x0072 como vector

de salto condicional.

27.1us – 28.3us. Reiniciación de la rutina principal. Un ciclo de reloj después, se tiene que PC =

0x0034.

28.3us – 63.4us. Repetición de la rutina principal hasta ocurrencia de interrupción por contador

externo. Se observa que el cálculo de la serie continuó: R3 aumentó, ahora R3 = 0x0003, R2 =

0x0003, R1 = 0x0002. Se observa que R4 aumentó, R4 = 0x0003.

63.8us – 64.4us. Carga de vector de interrupción para interrupción 1.

64.4us – 70.4us. Ejecución de rutina de atención por interrupción 1, y retorno desde interrupción.

70.4us – 78.0us. Ejecución de rutina principal. Revisión de ocurrencia de interrupción. Salto a

rutina de almacenamiento de datos en RAM.

78.0us – 91.0us. Ejecución de rutina de almacenamiento de datos en RAM. Retorno a rutina

principal. Se observa que R10 aumenta, R10 = 0x0001.

91.0us – 172.0us. Repetición de la rutina principal hasta ocurrencia de interrupción por contador

externo. Se observa que el cálculo del a serie ha continuado. Ahora se tiene que R3 = 0x0022, R2

= 0x0022, R1 = 0x0015, R4 = 0x0007. Se observa que entre una interrupción y la siguiente

alcanzan a ocurrir entre 4 y 5 cálculos de la serie.

172.6us – 178.8us. Ejecución de rutina de atención a interrupción 1 y retorno.

178.8us – 509.0us. Ejecución del ciclo principal, con interrupciones y rutinas secundarias, hasta

que ocurre excepción por overflow aritmético. Se observa que los últimos dos valores calculados

de la serie son R3 = 0x6FF1 y R2 = 452F, cuya suma genera el valor 6FF1+452F=B520. Este

valor genera overflow aritmético, pues el máximo valor positivo que se puede obtener es 7FFF.

También se observa que se han calculado un total de R4 = 0x0016 valores de la serie.

509.0us – 509.4us. Carga del vector de excepción para overflow aritmético en el Program

Counter.

509.4us – 512.4us. Ejecución de rutina de atención a excepción por overflow aritmético, y retorno

a rutina principal. Se observa la reiniciación de la serie, con R3 = 0x0001, R2 = 0x0000.

522.0us. Salto a rutina de carga de datos desde memoria RAM. Se observa el uso de R5 = 0x0072

como vector de salto condicional.

522.0us – 547.0us. Ejecución de rutina de carga de datos desde memoria RAM, hasta ocurrencia

de interrupción por contador externo. Se observa la carga de datos desde memoria: la dirección

R10 = 0x0203 entrega el dato R5 = 0x0012; la dirección R10 = 0x0202 entrega el dato R6 =

0x000D. Su resta genera R12 = 0x0005 cálculos por cada interrupción. Esto indica que, en efecto,

como se indicó anteriormente, ocurren aproximadamente 5 cálculos de la serie en el tiempo que

toma en entrar una interrupción al sistema.

90

547.0us – 607.2us. Ejecución de rutina principal hasta ocurrencia de interrupción por contador

externo. Se observa que la serie aumenta otra vez su valor: R3 = 0x0003, R2 = 0x0002, R1 =

0x0002. Se han realizado hasta el momento de ejecución del programa un total de R4 = 0x0019

cálculos aritméticos para la serie.

607.2us – 613.8us. Ejecución de rutina de atención a interrupción 1 y retorno.

613.8us – 716.7us, fin de simulación. Ejecución del programa repite el patrón mostrado hasta el

momento. Se observa que el cálculo de la serie va en R3 = 0x0022, y sea han realizado un total de

R4 = 0x001E cálculos. Además, el apuntador a la lista en memoria RAM está en la posición

0x0206, lo que indica que se han atendido un total de 5 interrupciones durante la ejecución.

Esto finaliza el archivo Sistema_HW.vwf.

9.8. Anexo: Código VHDL del Sistema, Distribución del

Directorio de VHDL en el Disco Adjunto

Todo el código VHDL del sistema se encuentra incluido en la siguiente carpeta del disco adjunto:

o root:\VHDL

A continuación se muestra un diagrama de tallos y hojas que representa la estructura de directorios de

la carpeta mencionada, junto con una descripción del proyecto que se encuentra en cada una.

Cada una de las carpetas descritas contiene un proyecto. Para abrir cada proyecto, debe buscarse

dentro de la carpeta, un archivo con el mismo nombre de la carpeta, y terminado en .qpf. Por ejemplo,

para abrir el proyecto CLAU_4, se debe buscar el archivo

root:\VHDL\RECURSOS\CLAU_4\CLAU_4.qpf.

VHDL: Todo el Sistema

o CONTADOR: Contador síncrono de 10 Bits.

o MUX_BUS_READ: Multiplexor a la entrada del bus de lectura del núcleo, para pasar

datos de la ROM o la RAM, dependiendo de qué dirección se lea.

o NUCLEO: El núcleo, implementado sin memorias ni periféricos.

o RAM: Unidad de memoria RAM de 4096 posiciones.

o RECURSOS: Bloques básicos usados repetidamente a lo largo de todo el diseño.

CLAU_4: Unidad de carry look-ahead de 4 bits.

FA_GP_1: Full adder de 1 bit, con señales Generate y Propagate.

FA_LAC_4: Full adder con unidad de carry look-ahead, de 4 bits.

FF_D_N: Flip Flop con Enable que reacciona a borde de bajada del reloj.

FF_D_P: Flip Flop con Enable que reacciona a borde de subida del reloj.

HA_1: Half adder de 1 bit.

MUX_2_1: Multiplexor 2:1 de 1 bit.

91

MUX_4_1: Multiplexor 4:1 de 1 bit.

MUX_8_1: Multiplexor 8:1 de 1 bit.

MUX_16_1: Multiplexor 16:1 de 1 bit.

REGISTRO16: Registro de 16 bits.

o ROM: Unidad de memoria ROM de 512 posiciones.

ROM_PROT.mif: Archivo de inicialización de la memoria ROM, directamente

editable con la herramienta Quartus II, e incluido en el proyecto. Se abrirá una

ventana con filas y columnas, representando las posiciones de memoria.

o SISTEMA: Sistema de prueba, con núcleo, RAM, ROM y contador síncrono.

Sistema_HW.vwf: Archivo de forma de ondas digitales que muestra el resultado

de la prueba del sistema sobre el chip FPGA. Archivo capturado por medio del

analizador lógico embebido sintetizado por la herramienta de Altera SignalTap II,

incluida en el entorno de desarrollo Quartus II, a través de la conexión JTAG

entre el chip FPGA y un puerto USB del computador, usando el periférico USB

Blaster incluido en el kit de desarrollo con el cual se trabajó.

o U_CONTROL: Máquina de control del núcleo.

o U_DATOS: Unidad de datos del núcleo.

ALU: Revisar secciones 4.1.3.1, 9.1.

U_ARIT: Revisar sección 9.1.1.

× NEGADOR: Revisar sección 9.1.1.1.

× SEL_CARRY: Revisar sección 9.1.1.3.

× SEL_OP2: Revisar sección 9.1.1.2.

× SUMADOR: Revisar sección 9.1.1.4.

U_AND: Revisar sección 9.1.2.

U_OR: Revisar sección 9.1.2.

LOGICA_COMPARACION_CERO: Revisar sección 9.1.5.

LOGICA_OVERFLOW: Revisar sección 9.1.3.

U_NOR: Revisar sección 9.1.2.

ALU_SEL_OUT: Revisar sección 9.1.6.

SEL_COMPARACION: Revisar sección 9.1.4.

R_BANK: Revisar secciones 4.1.4.1, 9.2.

BANCO: Revisar Secciones 9.2.1, 9.2.2.

× BANK_DEC: Revisar sección 9.2.2.1

92

× MUX_DEC: Revisar sección 9.2.2.2.

× MUX_PC: Revisar sección 9.2.1.3.

× PC_ADD: Revisar sección 9.2.1.2.

MUX1: Revisar sección 9.2.3.1.

MUX2: Revisar sección 9.2.3.1.

MUX_OP1: Revisar sección 9.2.3.2.

MUX_R_BANK_IN: Revisar sección 4.1.3.5.

MUX_SR_IN: Revisar sección 4.1.3.4.

MUX_BUS_DIR_IN: Revisar sección 4.1.3.6.

R_ALU: Revisar sección 4.1.4.5.

R_INST: Revisar sección 4.1.4.2.

R_IRQ: Revisar sección 4.1.4.6.

SR: Revisar sección 4.1.4.3.

SR_C: Revisar sección 4.1.4.4.

OPCODE_DET: Revisar sección 4.1.3.2.

ILEGAL_OP_DET: Revisar sección 4.1.3.3.