Story Transcript
Arquitectura de Sistemas Paralelos 3º Ingeniería Técnica en Informática de Sistemas Boletín de problemas del tema “Introducción a los sistemas de entrada/salida”
1. Para recibir datos de un periférico (con una tasa media de 100 bytes por segundo) se pueden usar dos métodos: • Hacer un muestreo (polling) cada milisegundo, muestreo que ocupa la CPU un total de 1µs. • Usar interrupciones, con un tiempo de respuesta (desde que se activa la señal de interrupción al llegar un byte hasta que se entra en la rutina de servicio) de 1µs. El tiempo de leer un byte se supone despreciable. ¿Qué método es preferible desde el punto de vista del uso de CPU? ¿Cuál debe ser el periodo de muestreo para que el método preferible sea otro?
2. Suponga un dispositivo E/S, con un único puerto de entrada de 8 bits, con el bit más significativo (MSB) normalmente a 1. Cuando baja a 0, indica que ha llegado un nuevo dato por los 7 bits restantes. Al leerlos, automáticamente el MSB vuelve a 1. Escriba una función que espere un dato y lo devuelva en una variable char.
3. El siguiente código se corresponde con una rutina de servicio de interrupciones para un periférico X, que está listo para realizar la operación de E/S cuando su registro de estado tiene el bit menos significativo a 1. Sin embargo, la rutina contiene un error. Explique muy brevemente la razón del error y su posible solución. void interrupt rutina_servicio () { ... while((inportb(STATUS_PORT_X)& 1)== 0) ; operacion_ES();
}
4. Un procesador determinado precisa de 1000 ciclos para llevar a cabo un cambio de contexto para comenzar a ejecutar una rutina de servicio de interrupciones y el mismo número de ciclos para retornar al programa que estaba ejecutándose. Supongamos un dispositivo E/S que realiza 150 peticiones por segundo, requiriendo cada una de ellas 10.000 ciclos para finalizar la operación de E/S. Por defecto, el procesador muestrea cada 0.5ms, tarea que consume 500 ciclos. Se pide: a) ¿Cuántos ciclos por segundo emplea el procesador en gestionar la E/S si se emplean interrupciones? b) ¿Y si se usa polling? Asuma que el procesador sólo muestrea cuando no hay programas de usuario ejecutándose (no hay que añadir cambios de contexto). c) ¿Cuál debe ser el periodo de sondeo para que esta técnica dedique tantos ciclos por segundo como las interrupciones?
5. Tenemos un escáner conectado a un computador cuyo procesador es de 50 MHz. Se pide comparar qué método, DMA o interrupciones, sería más adecuado desde el punto de vista del uso de CPU. Por interrupciones, el tiempo consumido para cada transferencia de 32 bits, incluyendo tanto la ejecución de la rutina de servicio como el tiempo necesario para servirla, es de 200 ciclos de reloj. Por DMA, programar cada transferencia de 2 KB supone 2000 ciclos de reloj y el tratamiento de la interrupción al terminar otros 1000 ciclos. Ignorar cualquier impacto de la conexión del bus entre el procesador y controlador de DMA. 6. El ADC0808 es un conversor analógico-digital de 8 entradas analógicas. Para leer el valor digital de uno de sus canales analógicos hay que seguir los siguientes pasos:
Boletín de problemas del tema “Introducción a los sistemas de entrada/salida”
1
• • •
Seleccionar el canal con los bits IA02 y activar la señal Addres Latch Enable (AEN) Activar la línea START para que comience el proceso de conversión. Cuando finaliza la conversión se activa la señal End Of Conversion (EOC) indicando que el valor digital puede ser leído en las líneas D0-7
Diseñar una interfase entre este dispositivo y un sistema 80x86 usando un 82C55. Escribir una rutina que reciba como parámetro el número de canal analógico que se quiere leer y que devuelva el valor digital correspondiente.
7. El controlador del puerto paralelo existente en las arquitecturas PC está pensado, en principio, para ser usado para la conexión de impresoras. Un PC suele disponer de dos puertos paralelos: LPT1 y LPT2. El primero suele tener como dirección base la 378h y el segundo la 278h. Dispone de los siguientes registros: Registro de datos: 8bits, solo lectura, posición 0 (378h en LPT1 y 278h en LPT2) Registro de estado: 8 bits, solo lectura, posición 1 (379h en LPT1 y 279h en LPT2)
Registro de control: 8 bits, lectura/escritura, posición 2 (37Ah en LPT1 y 27Ah en LPT2)
Se pide programar una rutina en C que reciba como parámetro una cadena de caracteres y la envíe a la impresora a través del puerto paralelo. Se debe implementar tanto la versión por polling como por interrupciones.
Boletín de problemas del tema “Introducción a los sistemas de entrada/salida”
2
Soluciones 1.- El periférico envía 1 byte cada 10ms. Puesto que el muestreo (polling) se realiza cada ms, gastamos 1µs cada ms (haya dato o no), mientras que con interrupciones (que sólo se activan cada vez que llega un byte) gastamos 1µs cada 10ms, diez veces menos. Por tanto es preferible en este caso usar interrupciones. Incluso si se reduce la frecuencia de muestreo a 1 cada 10ms, el caso óptimo, como mucho igualamos a las interrupciones debido al hecho de que el costo del muestreo es el mismo que el de la rutina de servicio. Sin embargo, téngase en cuenta que según otros parámetros el razonamiento puede ser distinto. Por ejemplo, con interrupciones el tiempo de respuesta es casi inmediato, mientras que el muestreo puede no coincidir con el momento justo de la llegada del dato (se usan buffers), por lo que desde el punto de vista de cada dato aislado las interrupciones seguirían siendo más rápidas. En resumen, sólo cuando el tiempo de respuesta a interrupciones sea mayor que el de muestreo y el periodo de muestreo se ajuste suficientemente al tráfico de datos será preferible usar muestreo desde el punto de vista de uso de la CPU.
2.char recibe() { while((inportb(PUERTO)&0x80)!=0); return (inportb(PUERTO)&0x7F); }
3.- No se debe hacer una espera activa dentro de una rutina de servicio, y mucho menos sin algún contador que detecte si se ha estado demasiado tiempo esperando. Por el propio mecanismo de interrupciones, si se ha entrado en la rutina de servicio es porque ha ocurrido una interrupción hardware, lo que de por sí indica que el dato ya está disponible. Si acaso, se podría hacer una comprobación (probablemente innecesaria) del tipo: if ((inportb(STATUS_PORT_X)& 1)!=0) operacion_ES();
4.a) (1000+10000+1000) ciclos * 150 peticiones = 1,8*106 ciclos/seg. b) El periodo de sondeo es T = 0.5ms, luego hay 1/T = 2*103 sondeos por segundo. Cada uno de ellos requiere 500 ciclos, pero de ellos sólo 150 (en un segundo) requieren además los 10000 ciclos de la operación E/S, luego: 2*103*500+150*10000 = 2.5*106 ciclos/seg. c) Debe ser (1/T)*500+150*10000=1.8*106, luego T = 1.67ms 5.a) Por interrupciones 200 ciclos 50. 106 ciclos (1 seg)
→ 32 bits → x
x = 32*50*106/200 = 8*106 bits/seg = 106 bytes/seg b) Por DMA 2000 + 1000 ciclos 50. 106 ciclos (1 seg)
→ 2048 bytes → x
x = 2048*50*106/3000 = 34.13*106 bytes/seg
Boletín de problemas del tema “Introducción a los sistemas de entrada/salida”
3
5.- (Continuación) Conclusiones: La transferencia por DMA es 34.13 más rápida que por interrupciones. Pero para que esto sea cierto debe ser de un tamaño suficientemente grande, pues, independientemente del tamaño, la transferencia por DMA consume 3000 ciclos. ¿A partir de qué valor conviene más usar DMA? x * 200 > 3000 → x >15 (15*32 = 480bits = 60bytes) A partir de 15 transferencias de 32 bits (60bytes) compensa utilizar DMA.
6.- Para conectar el ADC0808 al 82C55 vamos a utilizar el modo 0. El puerto A puede usarse como entrada de datos (D0-7), la línea PC4 para comprobar la señal EOC, y el puerto B para las líneas IA-02, AEN y START. Por tanto, el A se configura como entrada, el B como salida y la parte alta de C como entrada.
WR OE#
void inicializa_adc() { outportb (CONTROL, 0x98) outportb (PORTB, 0); }
/* Configura todos los puertos en modo 0. PA y PCH como entrada, el resto como salida */ /* Desactiva START y AEN */
unsigned char lee_adc (unsigned char canal) { unsigned char estado; /* Selecciona el número de canal analógico de entrada. */ canal = canal & 0x07; /* El canal debe estar entre 0 y 7 */ outportb (PORTB, (inportb (PORTB) & 0xF8 ) | canal); /* Se pone el canal */ outportb (PORTB, (inportb (PORTB) | 0x10)); /* Se activa AEN */ outportb (PORTB, (inportb (PORTB) | 0x08)); /* Se activa START */ outportb (PORTB, (inportb (PORTB) & 0xE7)); /* Se desactivan AEN y START */ estado = inportb (PORTC); while ((estado & 0x10) != 0) /* Esperamos mientras EOC=1 */ estado = inportb (PORTC); return (inportb(PORTA)); /* Devuelve el valor digital */ }
Boletín de problemas del tema “Introducción a los sistemas de entrada/salida”
4
7.a) E/S programada #define #define #define #define
DATOS 0x378 ESTADO 0x379 CONTROL 0x37A MAX_TEXTO 10000
int imprimir_texto(char *texto) /* Devuelve un código de error */ { int indice=0; int es_fin; inicializar_paralelo(); es_fin=fin(texto,indice) while(!es_fin) { envia_caracter(texto[indice]); indice=indice+1; es_fin=fin(texto,indice); } if (es_fin>0) return es_fin; /* Si hay error */ else return 0; } void inicializar_paralelo() { outportb(CONTROL,0x0C); /* Se activa la impresora, se resetea y se deshabilitan las interrupciones */ } int fin_transmision(char *texto, int indice) { unsigned char estado; estado=inportb(ESTADO); if ((estado|0xF7)==0xF7) /* ERROR=0? */ return 1; if ((estado&0x20)!=0) /* PAPEL_ERROR=1? return 2; if ((estado&0x10)!=0) /* ON_LINE=1? return 3;
*/
*/
if (texto[indice]==’\0’ || indice>=MAX_TEXTO) return -1; return 0; } void envia_caracter(char caracter) { while(inportb(ESTADO)|0x7F)!=0x7F); /* BUSY=0? */ outportb(DATOS,caracter); /* Se escribe el dato */ outportb(CONTROL,inportb(CONTROL)|0x01); /* STROBE=1 */ while((inportb(ESTADO)&0x40)!=0); /* ACK=0? */ }
Boletín de problemas del tema “Introducción a los sistemas de entrada/salida”
5
b) E/S por interrupciones …. /* Variables globales: la rutina de interrupción no tiene parámetros */ char texto[MAX_TEXTO]; int indice; int esfin;
…. int imprimir_texto(char *texto) /* Devuelve un código de error */ { void interrupt (*manejador_antiguo)(); antiguo_manejador=getvect(0x0F); disable(); setvect(0x0F,envia_caracter_int); enable(); inicializar_paralelo(); indice=0; if(!fin_int()) /* La función fin_int es semejante a fin excepto que usa variables globales en vez de parámetros */ { /* Solo se envía el primer carácter */ outportb(DATOS,texto[indice]); outportb(CONTROL,inportb(CONTROL)|0x01); /* STROBE=1 */ }
while(!esfin); /* Esta espera activa puede sustituirse por una función que duerma el proceso. Éste despertará mediante una señal cuando finalice la transmisión. Mientras el proceso duerme otro proceso puede hacer uso de la CPU */ disable(); setvect(0x0F,antiguo_manejador); enable(); if (es_fin>0) return es_fin; else return 0; } void inicializar_paralelo_int() { outportb(CONTROL,0x1C); /* Se activa la impresora, se resetea y se habilitan las interrupciones */ } void interrupt envia_caracter_int() { indice=indice+1; es=fin_int(); if (!es) { /* Se envía el siguiente carácter. No se comprueba el ACK pues éste es el que provoca la interrupción */ outportb(CONTROL,texto[indice]); /* Se escribe el dato */ outportb(CONTROL,inportb(CONTROL)|0x01); /* STROBE=1 */ } }
Boletín de problemas del tema “Introducción a los sistemas de entrada/salida”
6