ARQUITECTURA DE SISTEMAS PARALELOS. 3º INGENIERIA TECNICA EN INFORMATICA DE SISTEMAS. BOLETIN TEMA 5 (Sistema de entrada/salida). Curso 04/05. 1. 2. 3. 4. 5. Conteste muy brevemente : a) ¿Es posible entrada/salida mapeada en memoria en un procesador con E/S separada como el 8086?¿Qué ventajas e inconvenientes tendría? b) ¿Es posible crear un espacio de entrada/salida separado del espacio de memoria en un procesador con E/S mapeada como el 68000? En caso negativo, ¿Qué sería necesario modificar en el procesador tanto desde el punto de vista hardware como software? Implementar un sistema para control de una impresora mediante el protocolo centronics, usando un 82C55 (conexionado y software). Diseñar un controlador de interrupciones sencillo de una sola línea para un 8086. Debe activar la entrada INTR del 8086 cuando se reciba una interrupción y colocar un vector de 8 bits en D0-7 cuando el procesador active su salida INTA. El controlador de interrupciones debe permitir cambiar el vector por software. Se tiene un sistema basado en 8086 con un 8255 con ambos grupos en modo 1. Tanto por el puerto A como por el B pueden recibirse datos (entrada) usando el protocolo handshake, activándose la línea de interrupción correspondiente. La línea INTR del procesador debe conectarse a estas líneas, y mediante software detectar cuál es la que realmente se ha activado. Siempre debe darse prioridad a la entrada de datos por el puerto A. En ambos casos el número (tipo) de la interrupción es fijo e igual a FFh. Se pide describir cómo sería la conexión de las señales del 8255 con el procesador, así como escribir el software necesario, que debe incluir la inicialización del 8255 y la rutina de servicio. Se quiere construir un sistema de comunicación serie síncrona entre dos dispositivos, usando dos líneas: R y D. Los datos se reciben bit a bit por la línea D, con un nuevo bit por cada flanco de bajada del reloj R, y comenzando por el más significativo. Si no se reciben datos, la línea de reloj permanece a 1. Por ejemplo, el dato 11000100b: Suponga que el reloj R es mucho más lento que el del procesador. Suponiendo que se utiliza un 82C55 para recibir los datos, escriba el programa que permita inicializar el 82C55 y leer un byte. 6. En un computador basado en 8086 se quiere incorporar un display LCD de una línea de 16 caracteres. El controlador de display dispone de 7 líneas de datos (I0-6), una entrada de comienzo de operación (START#), y una salida de fin de operación (END#). Para escribir un carácter en el display se coloca su código ASCII en las entradas de datos y, después de un mínimo de 2 microsegundos, se activa START#. Cuando se detecte END# activa, se desactivará START#. El ciclo termina cuando END# vuelve a "1". El carácter escrito aparece en la posición actual del cursor, y el cursor avanza una posición a la derecha. Para controlar la posición se escriben códigos especiales: 0 para limpiar el display, 1 para avanzar a la derecha y 2 para retroceder a la izquierda. Se pide: • Dibujar la conexión entre el 82C55 y controlador de display. • Escribir una función para escribir en el display un carácter. ¿Cómo se escribiría una cadena de caracteres? 7. Diseñar un sistema basado en un temporizador 8254 donde se produzca una interrupción periódica cada 4 segundos. Suponga que se dispone de un reloj de 2MHz. 8. Usando un 82C55 y un 82C54 realizar el hardware y software que permita, a un sistema basado en 8086, enviar datos por una línea serie asíncrona a 1200 bits por segundo utilizando las interrupciones del procesador y un reloj de 10MHz. 1 Ayuda para la solución de problemas no resueltos en clase: Problema 1. a) Puede ser posible pero implicaría la existencia de una incoherencia en el software. Así, se tendrá que hacer código diferente en función de dónde se encuentre el periférico. Como posible ventaja se podría pensar, que además de utilizar de la E/S independiente, la E/S mapeada en memoria hace que el sistema sea más expandible. b) En el microprocesador haría falta al menos una señal que indique a qué espacio de espacio de memoria se accede. En cuanto al software (instrucciones) todo el software habría de ser ajustado a las nuevas instrucciones. Además, el S.O. tendría que cambiar su mapa de memoria y un nuevo mecanismo para poder gestionar tanto el viejo espacio de E/S como el nuevo. Problema 2. (Ver fotocopias de transparencias de clase). Problema 3. Nota: Cuando se utiliza un 8255 mediante interrupciones, es necesario añadir algún hardware para que el procesador lea correctamente el número de la interrupción. Esta tarea es normalmente realizada por un controlador de interrupciones. En este problema se diseña una versión muy simple de este controlador, con el único objetivo de ayudar a la comprensión de cómo funcionaría el sistema completo en presencia de interrupciones. Es conveniente recordar que no está entre los objetivos de la asignatura el realizar diseños hardware, y por tanto esta solución se incluye aquí a título meramente ilustrativo. Cuando el 8086 recibe una petición de interrupción por su entrada INTR, termina la instrucción que esté ejecutando en ese momento e inicia un ciclo de aceptación de interrupción. El ciclo de aceptación consiste en dos accesos. En el primero el procesador activa la salida /INTA (ACK de interrupciones), aunque no lee nada. En el segundo también activa /INTA, pero esta vez lee el número del vector de interrupción por las entradas D0-7 del bus de datos. El controlador de interrupciones de este problema es el más sencillo que se puede utilizar en un 8086, ya que sólo hay un vector de interrupciones asociado a la activación de INTR. Aun así debe permitir : • Modificar por software el número del vector de interrupción. La forma más sencilla es mediante una escritura a una posición de entrada/salida. Para ello se utiliza algún dispositivo de almacenamiento (un registro LS373 por ejemplo), y una lógica que lo cargue desde el bus de datos cuando se escriba (/WR activo) a la posición asignada al controlador de interrupciones (determinada por la decodificación, que no se muestra aquí). Es decir, este registro ocupará una posición en el mapa de E/S. • Colocar un vector en el bus de datos cuando se active /INTA. La salida del registro debe ser de tipo triestado, de forma que sólo coloque el vector en el bus cuando /INTA esté a 0. Problema 4. Para ver la forma programar y conectar en modo 1 (y entrada) el 8255 hay que acudir a las especificaciones técnicas de este chip. Antes de la conexión a los buses y al procesador hay que asignar al 8255 una serie de puertos de E/S y añadir la circuitería necesaria para que se produzca la codificación adecuada. Siguiendo las especificaciones del 8255 todo esto anterior se puede hacer fácilmente. Como esta circuitería no se pide en el enunciado no se tendrá en cuenta. Se decide tomar como puertos de E/S las direcciones que van de la 3FCh a la 3FFh (incluidas en el bus de expansión del mapa de E/S del i80x86). 2 Lo más complicado puede ser el hardware relativo a la gestión de las interrupciones. En el enunciado se dice que pueden recibirse interrupciones de los puertos A y B. Esto implica que se tienen dos salidas de interrupciones INTRA e INTRB. Sin embargo el i80x86 sólo tiene una única entrada para todas las interrupciones. ¿Cómo conectar las dos salidas a la entrada de interrupciones del i80x86? Respuesta: usando una puerta OR (ver esquema en la siguiente figura). Se supone que no se dispone de un PIC tipo 8259. Si así fuese se podrían conectar directamente estas salidas al PIC y este luego al 8086. Además se necesita un registro para guardar el identificador o vector de la interrupción (ID). Este valor siempre va a ser fijo (FFh) y por ello no se necesita un registro para guardar el ID de la interrupción. Se puede por tanto utilizar un buffer triestado (no programable) o también un registro de 8 salidas. No se necesita por tanto lógica o circuitería de selección del buffer triestado ya que no es necesario cargarlo con nuevos valores (ver esquema en la siguiente figura). Si se dispusiera de un PIC como el 8259 este buffer sobraría ya que se podría programar para que fuese él mismo el que se dejase el vector de interrupción en el bus de datos. El circuito completo queda de la siguiente forma: En cuanto al software necesario para implementar la E/S por interrupciones se necesitan dos rutinas. La primera de ella ha de ser de inicialización y la otra de atención a la interrupción. La rutina de inicialización tiene que inicializar/configurar el funcionamiento de los grupos/puertos del 8255, habilitar la generación de interrupciones del 8255 y alterar el vector de distribución de funciones para que cuando el buffer triestado/registro devuelva el valor/identificador FFh correspondiente a la rutina de atención a 3 la interrupción. En este último caso la CPU tendrá que ir a leer el elemento 255 (FFh) del vector de distribución de funciones y ejecute la rutina a la que apunta ese elemento. Esta rutina como ya se ha dicho será la rutina de tratamiento de la interrupción. #define PORTA 3FCh #define PORTB 3FDh #define PORTC 3FEh #define CONTROL 3FFh /* Rutina de inicialización */ void Inicializar () { disable (); /* Inhibición de las interrupciones */ outportb (CONTROL, 0xBF); /* 0b 1011 1111 Establecimiento de modos del 8255 */ outportb (CONTROL, 0x09); /* 0b 0000 1001 INTGA = 1 */ outportb (CONTROL, 0x05); /* 0b 0000 0101 INTGB = 1 */ setvect (0xFF, Rutina_atencion); /* Alteración del vector de interrupciones */ enable (); /* Habilitación de interrupciones */ }; La rutina de servicio de la interrupción tiene que leer el dato que viene del puerto A ó B. Sin embargo antes debe averiguar cuál de los dos puertos ha producido la interrupción. Cuando un puerto recibe un dato activa una señal IBF para indicarle al periférico que ha llenado un buffer de entrada. Por lo tanto leyendo el valor de IBFA (C5) e IBFB (C1) se podrá averiguar de dónde procede la interrupción. Para ello habrá que hacer una lectura del puerto C. /* Rutina de atención a la interrupción */ void interrupt Rutina_atencion () { char a; if (intportb(PORTC) & 0x20) { /* IBFA = 1 */ a = inportb (PORTA); /* Ahora se operaría con el dato/byte ‘a’ */ } else if (intportb(PORTC) & 0x02) { /* IBFB = 1 */ a = inportb (PORTB); /* Ahora se operaría con el dato/byte ‘a’ */ } } Pudiera darse el caso de que los dos puertos hubieran generado interrupción. En este caso se gestionaría primero el puerto/grupo A. Si se quiere cambiar la estrategia de prioridad habría que hacerlo por software en la rutina de atención a la interrupción. Problema 5. Si se va a trabajar con un 8255 hay que decidir en primer lugar el modo en el que va a trabajar. En este caso las señales no se ajustan al funcionamiento de las señales de protocolo de comunicación (handshake) del 8255. Por lo tanto los modos 1 y 2 se descartan. El 8255 tendrá uno de sus grupos en modo 0 y entrada. Por ejemplo el grupo 1 asociado al puerto A. La conexión será: 4 La conexión a un microprocesador del 8255 que en este caso será por ejemplo el i8088 es como sigue: NOTAS: La señal ALE significa Address Latch Enabled y en los procesadores 8088 y primeros 8086 se utilizaban para multiplexar las líneas de datos y direcciones ya que compartían un mismo bus físico (señales AD0 a AD7). La señal servía para introducir en un latch la dirección de acceso a memoria mientras se recibían los datos a través de las mismas líneas o bus de direcciones. LA0 y LA1 son las señales de dirección A0 y A1 que salen del latch de almacenamiento de la dirección. En el anterior circuito no se ha incluido el hardware necesario para la generación de interrupciones. Se adoptará una solución basada en la E/S programada. Para recibir un dato el programa tiene primero que inicializar el 8255. Una inicializado el puerto A en el modo 0 de entrada se habrá de vigilar la señal de reloj para que cuando se produzca un flanco de bajada en la señal R, se recoja el dato que hay en la señal D. A continuación se habrá de almacenar el bit o valor leído de la señal D en alguna variable para ir componiendo el valor del número completo. En este caso se dice que el número va a ser un byte. El programa podría agruparse en una sola función ya que al trabajar con E/S programada no hace falta tener por separado una rutina de atención a la interrupción y otra de inicialización. El programa o función sería algo parecido a lo siguiente: 5 char RECIBIR () { char byte = 0; short int contador = 0; /* Inicializa el 8255. El primer bit siempre es uno. Los dos siguientes (00) indican el modo cero. El siguiente bit indica que el puerto es de entrada (1). El resto de bits no tienen importancia y en este caso se han dejado en cero. */ outportb (CONTROL, 0b1001 0000); do { byte << 1; /* Desplazamiento a la izquierda de un bit */ while ((inportb (PTOA) & 0x02) != 0); /* La señal R está en alta. Se espera a que baje */ byte = byte + inportb (PTOA) & 0x01; /* Lee la señal D */ contador ++; while ((inportb (PTOA) & 0x02) == 0); /* La señal R está en baja. Se espera a que vuelva a estar en alta */ } while (contador < 8); return byte; }; NOTA: PTOA y CONTROL son macros en C que indican la dirección del puerto A y el registro de control del 8255. Así por ejemplo tomado una serie de direcciones cualesquiera: #define PTOA 0x158 /* A1=0 y A0=0 */ #define CONTROL 0x15B /* A1=1 y A0=1 */ Se va analizar ahora parte del código de la rutina: ¿Qué indica la condición ( inportb(PTOA) & 0x02) !=0 ) ? Al recogerse el dato del puerto A viene con el formato con el que se ha decidido conectar las señales R y D al 8255: 0 1 2 3 4 5 6 7 - - - - - - R D Si se hace AND bit a bit con 0x02 el resultado será: AND - - - - - - R D bit a bit 0 0 0 0 0 0 1 0 = 0 0 0 0 0 0 R 0 Es decir, si R es 1, entonces el resultado es distinto de cero. La solución anterior es válida suponiendo que el periodo de la señal R es grande y da tiempo a que la CPU rastree la señal muchas veces. Ejemplo de rastreo de la señal por parte de la CPU: Si esto no se cumple se deberán emplear algunos circuitos extras como por ejemplo un registro de carga serie/paralelo (74299 por ej.) que fuese cargando datos según los flancos de bajada de la señal R. También se necesitaría un contador que fuese restando desde 7 hasta 0 para saber cuándo se hubiesen recibido ya todos los bits del byte y se activase también según los flancos de bajada de la señal R. Al final de la cuenta atrás (llegado a cero) el contador generaría una señal de interrupción. 6 Problema 6. En este caso se dispone de un dispositivo que recibe señales de datos y un par de señales asociadas al protocolo de comunicación (END# y START#). La solución podría ser relativamente sencilla empleando el PPI 8255 en modo 1. Sin embargo el 8255 sólo espera 350 ns antes de poner la señal de START# (OBF# en el 8255) a cero. De esta forma no se cumplirían los requisitos temporales del dispositivo de salida (en este caso un LCD). (Notación: SEÑAL# significa SEÑAL negada o en baja) La primera solución podría consistir en poner el 8255 en modo 0 y gestionar las señales de control mediante software. El esquema de conexión sería inmediato: Con este esquema se puede plantear la escritura en el LCD mediante E/S programada. De esta forma se evita tener que añadir más hardware al circuito para gestionar las interrupciones. La rutina que gestiona la escritura en el LCD debe primero inicializar el 8255. A continuación debe poner el dato en la salida del puerto A, esperar 2 µs y finalmente activar la señal de START#. La señal START# hay que desactivarla cuando se reciba la señal END# (ACK). Se van a escribir dos rutinas: una de inicialización y otra de escritura: /* Rutina de inicialización: */ void INIT () { outportb (CONTROL, 0x88); /* 0b1000 1000 Poner el puerto A en modo 0 y salida. Los 3 últimos bits no importan */ outportb (PTOA, 0x80); /* 0b1000 0000 Señal START# = 1, es decir, no se transmite ningún dato */ WRITE_LCD (‘0’); /* Limpia el LCD */ } /* Rutina de escritura */ void WRITE_LCD (char C) { outportb (PTOA, C & 0x7F); /* 0b 0111 1111 Se coloca el código ASCII a la entrada del bus de datos del LCD */ delay (1); /* Se espera 1 milisegundo. Como mínimo se pedía 2 µs */ outportb (PTOA, C & 0x7F); /* 0b 0111 1111 Señal START# = 0, es decir, el LCD ya puede recoger el dato */ while (inportb (PTOC) & 0x40 ¡=0); /* Se espera a la llegada de END# */ outportb (PTOA, C & 0xFF); /* Señal START# = 1 */ /* También valdría para este último paso usar la siguiente instrucción: outportb (PTOA, 0x80); 0b1000 0000 */ } 7 Otra solución al problema sería utilizar el modo 1 del 8255 y adaptar los tiempos por hardware para que pueda funcionar con el LCD. Para ello se ha de lograr que se retrase la señal START# al menos 2 µs. Esto se consigue con un circuito de cómo el siguiente: De esta forma no se tendrían que activar las señales OBF.A y ACK.A por software. Falta saber cuando ha terminado el ciclo de escritura del LCD. Cuando el LCD termina, manda un pulso a través de la señal END#: Por lo tanto el ciclo terminaría cuando START# = 1 y END# = 1. Eso se puede hacer leyendo el puerto C mediante un bucle de espera activa. El programa quedará ahora como sigue: void INIT () { outportb (CONTROL, 0xA0); /* 0b 1010 0000 Los últimos 4 bits no importan. Pone el puerto A en modo 1 y salida */ } void WRITE_LCD (char C) { outportb (PTOA, C); /* Se escribe el carácter ASCII en el puerto A */ while (inportb (PTOC) & 0xC0) ¡= 0xC0); /* Se espera a recibir la señal ACK# = END# */ } Problema 7. Se trata de una aplicación trivial del modo 2 del timer, si no fuera porque al ser el reloj de entrada de 2MHz, para esperar 4 segundos habría que inicializar el contador con 8x106, número que desde luego no cabe en 16 bits. La solución es utilizar dos contadores “en cascada”, es decir, conectando la salida out de uno a 8 la entrada clk de otro (ambos en modo 2), de forma que cuando se llegue al final de la cuenta del primer contador, se produce una bajada de su señal out que supondrá un pulso de reloj (un avance de la cuenta) para el segundo contador. Bastará con que el producto de los valores iniciales de los contadores sea 8x106. Problema 8. Aquí el timer también se usa en modo 2 con la salida out conectada a la entrada INTR del procesador (con un inversor, pues la salida en el modo 2 es un pulso a cero). Cada vez que se produzca un pulso en out se produce una interrupción del procesador. Hay que escribir la rutina de servicio que enviará un nuevo bit por la línea serie. Nótese que de nuevo es importante suponer que la velocidad del procesador es muy superior al ritmo al que hay que enviar los datos, para que dé tiempo a todo el proceso de interrupciones. 9