APUNTES ARQUITECTURA DE COMPUTADORAS UNLA

Anuncio
ARQUITECTURA DE COMPUTADORAS
Docentes:
Prof. Oscar Montes
Prof. Obadiah Oghoerore Alegbe
Prof. Roberto García
Tema:
Apuntes de la asignatura
Año:
2011
Procesos
En el modelo de procesos todo software ejecutable de la computadora, inclusive el sistema operativo, se organiza en varios procesos secuenciales que de aquí en adelante llamaremos PROCESOS. Un proceso es en general parte de un programa con determinadas características: incluye el
estado propio del proceso, además del estado del registro de la CPU y tiene la intención de ser
ejecutado por el SO (está cargado en RAM). Las antiguas CPUs sólo podían ejecutar un proceso
a la vez, pero como lo ejecutaban muy rápidamente, el operador podía tener la sensación de que
había muchos procesos ejecutándose a la vez. En este momento existen CPUs capaces de ejecutar
dos, tres y hasta cuatro procesos juntos, dependiendo de su estructura interna.
La diferencia entre un proceso y un programa es sutil pero crucial. Podríamos utilizar la siguiente
analogía para aclarar este punto. Consideremos un científico de la computación con una mente
culinaria, que está cocinando el pastel de cumpleaños de su hija. Él tiene una receta para un pastel
de cumpleaños y una cocina bien abastecida con los ingredientes necesarios. En esta analogía la
receta es el programa (un algoritmo expresado en cierta notación adecuada), el científico es el
procesador (CPU) y los ingredientes necesarios para el pastel son los datos de entrada. El proceso
es la actividad en la que el cocinero lee la receta, busca los ingredientes, y cocina el pastel (listo,
bloqueado, en ejecución).
Imaginemos ahora que un hijo del científico entra corriendo, llorando y diciendo que lo picó una
abeja. El científico registra el punto de la receta donde se quedó (el estado del proceso activo se
resguarda, se memoriza), busca un libro de primeros auxilios, comienza a leer las instrucciones
de éste y busca los elementos en el botiquín. El científico atiende la picadura de su hijo y luego
de que éste se calma, regresa al punto de elaboración del pastel.
El proceso entonces se puede entender como una actividad especial de cierto tipo, en donde hay
un programa, entradas y salidas de datos y tiene un estado determinado.
Estados de un proceso
Aunque cada proceso se debe considerar como una entidad independiente (contador de programa,
estado interno, uso de memoria, etc.) es frecuente que los procesos deban interactuar unos con
otros. Un proceso podría generar cierta salida que fuera la entrada necesaria para otro proceso.
Desde este modelo, se utilizan tres estados fundamentales:
a) Listo: el programa (o parte de él) se carga en la memoria de trabajo (RAM). La característica de estar cargado en RAM, es decir, la intención cierta del SO de ejecutar esas instrucciones, convierten al programa en un proceso.
b) Ejecución: en este estado la CPU tiene el control de las operaciones de ese proceso y las
va cumpliendo en un orden determinado. Este estado es propio del núcleo de la CPU, y
cada uno de ellos (si es que tuviera varios) puede tener a un proceso en este estado en un
tiempo muy breve que tomaremos como menor al tiempo de instrucción.
2
c) Bloqueado: cuando el proceso requiere de datos que se deban buscar en periféricos de entrada lentos (cuyo acceso involucre una cantidad razonablemente grande de tiempos de
instrucción) el SO pone al proceso en este estado de espera y conmuta a la CPU a otro
proceso.
Planificación de procesos
Cuando hay más de un proceso ejecutable, el SO debe decidir el orden de ejecución. Esta parte
del SO se denomina PLANIFICADOR, y la lógica para esas decisiones están implícitas en el
algoritmo de planificación.
En los tiempos pasados, los sistemas de procesamiento eran por lotes (batch), porque eran grupos
o lotes de tarjetas perforadas que se debían procesar desde el comienzo hasta el fin. Cuando se
pensó en multiprocesamiento, el planificador ya no era un operador, sino que debía ser algo que
tomara decisiones más rápidamente. El planificador debe poder atender a ciertas características
para su operación:
a) Equidad: garantizar que cada proceso obtiene un tiempo de CPU.
b) Eficacia: mantener a la CPU ocupada el 100 % del tiempo.
c) Tiempo de respuesta: minimizar el plazo de ejecución entre varios procesos.
d) Tiempo de regreso: minimizar el tiempo que deben esperar otros procesos para entrar en
ejecución.
e) Rendimiento: maximizar el número de tareas procesadas en la unidad de tiempo.
Un poco de reflexión muestra que algunas de estas condiciones se excluyen mutuamente. Se puede demostrar (Kleinrock 1975) que cualquier algoritmo de planificación que favorezca algún tipo
de tarea afecta a otra. Por ejemplo: para darle más tiempo de CPU a un usuario (proceso), hay
que darle menos a otro, ya que el tiempo de CPU es finito.
Una primera aproximación a un algoritmo de planificación será la elección entre dos modos de
asignación de la CPU: El modo apropiativo y el modo no apropiativo, vistos desde el SO, es
decir: si el SO va a poder conmutar la CPU en cualquier instante de la ejecución de un proceso
(apropiativa) o el proceso se va a ejecutar de principio a fin sin ceder el uso de la CPU (no apropiativa).
Algunos de los tipos más comunes de planificación son:
a) FCFS: (First Come First Served) es una cola de espera tradicional en la que los procesos
se van ejecutando en el orden de llegada. Esta cola se puede considerar aleatoria (al azar)
y por lo tanto no implica planificación alguna. Es en general el peor algoritmo de planificación para cualquier recurso, aunque también el más fácil de implementar.
b) Round Robin: este método es apropiativo y básicamente es el otorgamiento de la CPU
durante tiempos definidos denominados Quantum.
c) Por Prioridades: la hipótesis implícita en la planificación anterior es que todos los procesos tienen igual importancia. Esto no es cierto en la realidad, ya que el proceso de actualización del reloj no puede esperar. Un defecto propio de la ejecución por prioridades es
que procesos de muy baja prioridad lleguen a no ejecutarse jamás; este jamás es literal, ya
3
que en una universidad de EEUU se encontró por casualidad un proceso que llevaba meses esperando la ejecución. Esta “deficiencia” de este tipo de planificaciones se soluciona
habitualmente incorporando la variable “tiempo de espera” haciendo que la prioridad del
proceso se incremente automáticamente con el transcurso del tiempo.
d) Colas Múltiples: trata de solucionar algunos problemas de la planificación por prioridades, teniendo en cuenta una característica de los procesos que lleva a considerar el tiempo
necesario de ejecución dado por el uso de la CPU en operaciones matemáticas o la preponderancia del trabajo con datos externos. Estas clasificaciones se conocen con el nombre de “limitado por CPU” y “limitado por E/S”.
e) SJF (Shortest Job First): esta planificación tiende a mejorar el tiempo de respuesta, poniendo en ejecución primero los trabajos más cortos y luego los más largos; el tiempo de
regreso también se acorta, aunque el tiempo total es el mismo. Una desventaja importante
de este tipo de planificaciones es que el SO debería conocer con anticipación cuánto
tiempo de CPU requiere cada proceso, y el sistema no cuenta con capacidad de adivinación.
Interrupciones
En la analogía del científico computador pastelero, se hizo referencia a un cambio de procesos. Si
usamos una planificación del tipo de prioridades, que se considera apropiativa, el SO dependiendo de los procesos, deberá conmutar a la CPU en el instante en que un proceso de mayor prioridad necesite ejecutarse. En la figura de arriba, el servicio del disco es un proceso que tiene prioridad sobre el servicio de comunicaciones, éste es más prioritario que el servicio de impresora, y
éste es más prioritario que el programa en ejecución.
4
En una interrupción se debe guardar el estado de la CPU (los registros más importantes) y luego
comenzar la ejecución del proceso siguiente.
El Bloque de control del proceso o en inglés PCB (Process Control Block) es un registro especial
donde el sistema operativo agrupa toda la información que necesita conocer respecto a un proceso particular. Cada vez que se crea un proceso el sistema operativo crea el PCB correspondiente
para que sirva como descripción en tiempo de ejecución durante toda la vida del proceso.
Cuando el proceso termina, su PCB es borrado y el registro puede ser utilizado para otros procesos. Un proceso resulta conocido para el sistema operativo y por tanto elegible para competir por
los recursos del sistema sólo cuando existe un PCB activo asociado a él. El bloque de control de
proceso es una estructura de datos con campos para registrar los diferentes aspectos de la ejecución del proceso y de la utilización de recursos. La información almacenada en un PCB incluye
típicamente algunos o todos los campos siguientes:

Identificador del proceso (Process Identificator -PID-, de sus siglas en Inglés).

Estado del proceso. Por ej. listo, en espera, bloqueado.

Contador de Programa: Dirección de la próxima instrucción a ejecutar.

Valores de registro de CPU. Se utilizan también en el cambio de contexto.

Espacio de direcciones de memoria (registro base, registro límite). (Ver “Paginación”)

Prioridad, en caso de utilizarse dicho algoritmo para planificación de CPU.

Lista de recursos asignados (incluyendo descriptores de archivos y sockets abiertos).

Estadísticas del proceso (tiempos de ejecución históricos, limitación de la CPU).

Datos del propietario (owner), habitualmente denominado “padre/hijos”.

Permisos asignados.
Comunicación entre periféricos
Habitualmente se suelen denominar periféricos a elementos agregados fuera del gabinete de la
computadora, en nuestro caso nos centraremos en la CPU y usaremos ese vocablo para denominar elementos que se comunican con la CPU.
En la arquitectura Harvard, existen líneas de comunicación (buses) diferentes para los datos y el
programa, este tipo de arquitectura es el que usa el µC PIC; en el caso habitual de las PCs se usa
la arquitectura Von Neumann, cuya característica es que datos y programa están ubicados en la
RAM y por lo tanto comparten el bus de datos.
En las PCs existen tres buses diferenciados por su utilidad: el ya mencionado de datos, el de direcciones y el de control. Los de datos y direcciones quedan definidos por su nombre, el de control son todas las líneas de comunicación que no puedan ser definidas ni como datos ni como direcciones.
5
En la figura anterior se esquematiza el modelo de Harvard, siendo cada uno de los bloques un
periférico y uno de ellos la CPU.
El bus de datos puede incluir entre 32 y cientos de líneas, cuyo número se conoce como anchura
del bus de datos. Puesto que cada línea solo puede transportar un bit en cada instante, el número
de líneas determina cuántos bits se pueden transferir al mismo tiempo. La anchura del bus es un
factor clave a la hora de determinar las prestaciones del conjunto. Por ejemplo, si el bus de datos
tiene una anchura de 8 bits (1 byte) y las instrucciones son de 16 bits, entonces el procesador debe acceder al módulo de memoria dos veces por cada ciclo de instrucción y si busca un dato numérico tipo doble precisión, deberá acceder 4 veces.
Las líneas de dirección se utilizan para designar el emisor y el receptor de la comunicación y/o la
ubicación del dato o instrucción buscado (RAM). Claramente, la anchura del bus de direcciones
determina la máxima capacidad de memoria direccionable en forma directa del sistema. En algunos casos de esta estructura se posiciona el programa en la parte baja de la memoria y los datos
en la parte alta de la memoria, como la manera de hacer sencillo el cambio entre instrucciones y
datos, cambiando el bit más significativo (MSB).
Las líneas de control se utilizan como procedimientos pre y post comunicacionales (Handshaking).
Las señales de control transmiten tanto órdenes como información entre los módulos del sistema.
Entre las más comunes están las señales de: Memory Write; Memory Read; I/O Read; I/O Write;
diferentes señales tipo ACK (Aknowledge); Bus Request; Bus Grant; Interrupt Req; Interrupt
ACK; etc.
Temporización de los buses
Las comunicaciones se pueden clasificar en Síncronas o Sincrónicas y Asincrónicas. Las primeras están relacionadas directamente a un reloj (clock). Las comunicaciones en los buses son sincrónicas, aunque la frecuencia del reloj que las rige es diferente del reloj que comanda la CPU.
En la actualidad se pueden encontrar buses de 400 MHz y 800 MHz, cuando el de la CPU está
cerca de los 2000 MHz (2 GHz).
La figura siguiente representa en forma simplificada un ciclo de lectura/escritura de la RAM.
6
En el gráfico aparecen los datos del reloj, el bus de direcciones, el bus de control y el de datos.
Generalmente se utilizan los flancos de bajada de la señal para efectuar la operación indicada por
el nombre de la señal en cuestión.
La comunicación comienza en el bus de control con las líneas de estado y después de un breve
lapso aparecen la dirección del periférico (en un gran porcentaje de casos, las comunicaciones
involucran como emisor o receptor a la CPU). Los periféricos leen esta dirección en el flanco de
bajada de la señal “validación de dirección” y tienen un tiempo para compararla con su propia
dirección. Esta comparación resulta válida sólo para el periférico que debe intercambiar con la
CPU, en este caso, suponemos que es la RAM. Lo que sigue es la activación de la RAM en el
modo de lectura (desde el punto de vista de la CPU lectura implica requerir de la RAM un dato
específico), a partir de allí la RAM coloca el dato en el bus de datos “línea de datos” y con el
flanco descendente de la señal de lectura, la RAM le avisa a la CPU que puede copiar ese dato a
sus registros. En la operación de escritura es la CPU la que avisa a la RAM a través de la señal
“escritura” que el dato que está en el bus es el que se debe guardar.
Las señales de control involucradas en esta comunicación son las de: líneas de estado, validación
de la dirección, lectura y escritura.
Una vez completado este ciclo que, según la figura, ocuparía tres ciclos del reloj (7,5 nseg), los
buses de datos y direcciones se deberán ocupar de otras comunicaciones. Dado que el tiempo es
no-humano, un humano supondría que el bus lleva una cantidad de datos muy grande; este proceso se denomina “multiplexión”, en este caso, de tiempo.
Debido a la complejidad cada vez mayor del uso del bus de datos, se incorporó al sistema un
ÁRBITRO DE BUS.
7
Esta figura representa el método antiguo de arbitraje a través de un terminador de bus, en donde
cada periférico o bloque solicitaba el uso del bus a través de un contacto eléctrico. La ubicación
física de los bloques determinaba su prioridad, lograda por el tipo de conexión, denominado
Daisy Chain.
En la actualidad, el árbitro del bus es un periférico más al que acceden cada uno de los bloques
solicitando el bus de datos con una línea (control), para identificarse pueden utilizar una línea
cada periférico o el bus de direcciones. El árbitro compara esa dirección para otorgarle un nivel
de prioridad, y ésta a su vez, indicará cuándo puede usar el bus de datos para la comunicación
(ver esquema de Modelo Harvard).
Como estudio de caso, daremos algunas características del bus tipo PCI (Peripheral Component
Interconnect), patentado inicialmente por la empresa Intel en 1990 para su procesador Pentium,
posteriormente la patente fue cedida por Intel al dominio público, lo que motivó a muchos fabricantes de hardware a incorporar esas especificaciones a sus componentes. Como resulta de aplicación electrónica sencilla, y por lo tanto es barato, ha ganado popularidad rápidamente, suplantando a los anteriores (ISA, EISA, Local bus, etc.).
El Standard PCI es un bus de 64 bits de datos, con una frecuencia de reloj de 66 MHz y transferencias múltiples de datos (hasta 528 MB/seg o 4,224 Gb/seg.). Esto es decir que el árbitro (también centralizado) puede otorgar el bus a un periférico para que éste transmita un tren de datos
seguidos, dependiendo del tráfico de datos en cada instante. Este tipo de transferencia hace a la
eficiencia y rapidez de transmisión, características necesarias para adaptarse mejor a las frecuencias de la CPU.
8
A este bus se le pueden acoplar adaptadores para conectar periféricos más lentos o más rápidos
que las velocidades normalizadas.
El arbitraje es centralizado y sincrónico, utiliza un esquema maestro-esclavo (master-slave) y
prioridades para la cesión del bus. Cada dispositivo del bus PCI se conecta al árbitro a través de
una línea de petición identificada con un número que por simplificación supondremos relacionado al orden de prioridad de ese dispositivo; el árbitro contesta con una línea también numerada
para que el dispositivo reconozca su posibilidad de uso del bus.
Ciclos de instrucción
Cuando hablamos de los procesos los relacionamos con un programa. Habitualmente estamos
acostumbrados a programar en lo que denominamos lenguajes de alto nivel (el nivel se corres-
9
ponde con el grado de abstracción y, por lo tanto, el nivel más alto es el del programador y el
nivel más bajo la CPU); como esas instrucciones las debe entender la CPU lo que denominamos
compilador es el encargado de traducir las estructuras de alto nivel a niveles compatibles con la
CPU.
En los párrafos siguientes trataremos de conceptualizar cómo es una instrucción de bajo nivel
suponiendo una sentencia de asignación del tipo
A=B+A
Esta sentencia debe entenderse como: tomar el contenido de la dirección B de memoria e ingresarlo a uno de los registros de la CPU; tomar el contenido de la dirección A, ubicarlo en otro registro de la CPU; sumar los valores de los datos obtenidos y, por último, ubicar el resultado en la
dirección A (el dato previo de la dirección A se pierde).
El método que vamos a ejemplificar supone un direccionamiento directo de la memoria RAM
(más adelante veremos que hay otros modos de direccionamiento de la RAM).
Proponemos considerar que las primitivas del procesador usado son 16, lo cual implica códigos
de operación para la CPU de cuatro bits. En la actualidad se usa combinar en una instrucción los
códigos de operación y las direcciones de memoria. Para este esquema, si queremos direccionar
hasta 2.048 Bytes de RAM necesitaremos un formato de instrucción de 16 bits, como indica la
siguiente figura.
En la figura, los códigos de operación a usar se limitan a cargar la instrucción de bajo nivel al
registro acumulador desde la memoria; sumar a ese acumulador un dato desde la memoria y almacenar el resultado del acumulador en una posición de memoria.
En la figura siguiente se esquematiza la operación A=B+A simplificando el formato de instrucción a números decimales y suponiendo que A=941 y B=940, siendo éstas las direcciones que
contendrán los datos a sumar.
AC representa el registro Acumulador (o de trabajo para el PIC)
IR representa el registro del decodificador de instrucciones
10
Las posiciones de RAM están identificadas por su dirección y el contenido (en este caso se respeta el orden bajo para el programa y el alto para datos).
En el caso particular del µC PIC el formato de instrucciones es de 14 bits; 8 corresponden a las
direcciones y 6 a los códigos de operación.
Los pasos del esquema anterior involucran las siguientes operaciones:
Paso 1: el PC (Program Counter) tiene la dirección 300. En esa dirección existe la instrucción
compuesta de 1 (cargar AC desde la memoria) y 940, que es la dirección de memoria. Este dato
(1 940) se copia en el registro de instrucción y se decodifica: código de operación y dirección de
memoria.
Paso 2: se efectúa un ciclo de lectura de la RAM a la posicición 940 y se copia el dato de esa
dirección (valor 3) al acumulador.
Paso 3: el PC tiene la dirección 301. En esa dirección existe la instrucción compuesta de 5 941,
que se copia en el registro de instrucción y se decodifica: código de operación y dirección de
memoria (código de operación 5 es sumar al acumulador un dato de memoria)
11
Paso 4: ejecuta la suma entre el acumulador y el dato de la posición de memoria 941.
Paso 5: el PC (Program Counter) tiene la dirección 302. En esa dirección existe la instrucción
compuesta de 2 (almacenar el acumulador en una posición de memoria) y 941, que es la dirección
de memoria. Este dato (2 941) se copia en el registro de instrucción y se decodifica.
Paso 6: se ejecuta la instrucción 302, con lo cual se copia el contenido del acumulador a la posición de memoria 941.
Modos de direccionamiento
Ya sea por las operaciones a realizar o para lograr direccionamientos más amplios, existen varias
formas de que en una instrucción se haga referencia al dato de memoria. Algunos de esos modos
están graficados a continuación.
12
Direccionamiento Inmediato
Es la forma más sencilla de direccionamiento porque en realidad no es un direccionamiento. En
la instrucción (codop+operando) el código de operación siempre indica la operación matemática
o lógica a realizar. En el resto del código de la instrucción en este modo simplemente está el valor
para usar en la operación, es decir, no hay acceso adicional a memoria para tener el valor.
Direccionamiento Directo
13
Los bits que en la instrucción anterior contenían el operando ahora contienen una dirección física
de la memoria donde reside el dato a utilizar. La limitación de este direccionamiento está en la
cantidad de bits destinada a las direcciones.
Direccionamiento Indirecto
En este caso los bits contienen una dirección de memoria cuyo contenido es la dirección de memoria que contiene el dato. Con este esquema podríamos usar, por ejemplo, 4 bits en el campo de
direcciones de la instrucción (16 posibilidades) y a través de una tabla residente en RAM de 16
posiciones de 1 byte, accederíamos en realidad a 256 posiciones por cada una de las entradas de
la tabla, en total, 16x256=4096 posiciones. Dependiendo del microprocesador esta tabla puede
estar dentro de alguna zona de memoria interna con lo cual se ahorraría uno de los accesos a memoria externa.
Direccionamiento con Desplazamiento
Dentro de estos los más conocidos son el relativo y el indexado.
Relativo: Este direccionamiento requiere que las instrucciones tengan dos campos de direcciones; el valor contenido en uno de los campos es utilizado directamente y el otro campo de direcciones contiene habitualmente una referencia implícita. La dirección efectiva se consigue sumando la dirección explícita al contenido de la dirección implícita. Esto si bien es complicado de entender mediante texto, es fácilmente entendible si pensamos en una tabla de conversiones de doble entrada. La tabla estará ubicada en forma secuencial en memoria a partir de la dirección explícita de la instrucción. El desplazamiento a los otros renglones de la tabla se guarda en una posición de memoria referenciada indirectamente. Variando el valor de esta última posición de memoria, obtenemos el acceso a cada uno de los renglones de la tabla.
Indexado: En este caso la primera porción de la dirección es también relativa, es decir, referencia a una posición de la memoria externa.
Memoria
Cuando nos referimos a “Memoria”, estamos hablando, específicamente, de la RAM. Es uno de
los recursos más importantes de una máquina, ya que interviene en una relación muy estrecha con
el microprocesador. En forma básica podemos pensar la memoria física como un estante con muchos cajones y dentro de cada cajón lo que llamamos un dato; cada cajón se podrá abrir mediante
una dirección específica.
Administración de la Memoria
El esquema más sencillo es el direccionamiento directo, lineal, de la memoria física. Para este
tipo de direccionamiento no hay otra planificación más que comprobar si el espacio físico libre de
memoria permite cargar el programa y los datos que queremos ejecutar. Este tipo de esquema fue
el utilizado en las primeras computadoras para el modo monoprocesamiento.
14
Cuando se comenzó a pensar en mejorar la eficiencia de la CPU, esto llevó a considerar lo que se
llama multiprocesamiento. En este contexto la CPU se conmuta entre varios procesos (cada proceso podría pertenecer a un programa diferente); la RAM debe contener a los procesos y, por lo
tanto, la RAM va a estar compartida. Los algoritmos de administración de la memoria tienen sentido en el contexto de multiprogramación.
Multiprogramación lineal
Éste es el esquema más sencillo de la administración y se basa en usar la memoria para cargar la
mayor cantidad de procesos posibles. El SO no utiliza otro algoritmo que no sea el de buscar un
espacio lo suficientemente grande como para que quepa el proceso.
En la primera carga de la memoria se puede llegar a aprovechar valores muy cercanos al 100 %
de la memoria, pero a medida que el SO debe cambiar los procesos para su futura ejecución, y ya
que los procesos no ocupan todos el mismo lugar, van quedando huecos cada vez más pequeños y
distribuidos, lo que los convierte en inútiles (los procesos deben cargarse en posiciones contiguas
de la memoria). A este problema se lo llama Fragmentación Externa y puede dejar espacios muy
grandes de la memoria inutilizables.
La forma de solucionar esto es efectuando la compactación de la memoria. En este modelo el SO
debe analizar todo el espacio de memoria e ir reubicando procesos de manera de ir acumulando
esos huecos de manera de volverlos útiles. Este procedimiento ocupa mucho tiempo de CPU,
haciendo que el rendimiento del sistema disminuya notablemente.
Multiprogramación por páginas
La primera forma de solucionar la fragmentación externa es dividiendo el espacio de memoria
RAM en subespacios (frames), todos de la misma capacidad. Se suele denominar Frames a los
marcos de página, es decir, al espacio físico sin datos que conformará la página (datos). Este esquema tiene, como todos, sus ventajas y sus desventajas. Como ventajas principales están:
a) Un direccionamiento jerarquizado, tipo Árbol
b) La posibilidad de extender la memoria RAM a través de un concepto denominado Memoria Virtual, en el que se usa el disco como extensión de la RAM disponible.
Como desventajas se pueden citar:
a) Que el direccionamiento es más complejo y requiere apoyo de la arquitectura de la CPU
b) Dependiendo del tamaño de las páginas, aparecen en la última página de un proceso zonas
libres (a menos que el proceso ocupe exactamente un múltiplo entero de la capacidad de
las páginas), que se denomina fragmentación interna, y que, estadísticamente, podría estimarse en la mitad de la capacidad de una página.
c) Aparece el concepto de Dirección Lógica, que debe ser transformada a una dirección física, como vemos en el siguiente ejemplo:
15
En este esquema, los procesos pueden cargarse en zonas de memoria que no sean contiguas, la
que sí debe ser secuencial es la tabla de páginas. Dentro del programa, cada dirección lógica está
constituida por un número de página y una dirección relativa dentro de la página (desplazamiento).
La tabla de páginas está implícita en los PCB, en la forma de registro Base y registro Límite.
Existen mecanismos dentro de la arquitectura del procesador que verifican que las direcciones
físicas obtenidas sean congruentes con esta tabla de página, para evitar que el Program Counter
esté direccionado a una dirección de memoria errada.
El esquema siguiente representa el modo de direccionamiento del Pentium con un poco más de
precisión.
16
Algoritmos de Reemplazo de Páginas
Utilizando el concepto posible dentro de paginación que era la memoria virtual, se presenta el
problema de una memoria RAM escasa extendida por las páginas ubicadas en el disco. El problema implica saber qué páginas de la RAM puedo borrar cuando necesito cargar páginas del
disco.
El SO, cuando termina de ejecutar las instrucciones de una página, necesita continuar con la siguiente en forma secuencial. Ahora bien, una vez que termine de ejecutar las instrucciones de
esta nueva página, las preguntas son: qué página va a necesitar, y si esta página ya está cargada
en la RAM o hace falta borrar una de las páginas usadas para cargar la necesaria del disco (Fallo
de Página).
Detallaremos a continuación algunos algoritmos útiles para los fallos de página:
a) Reemplazo sencillo tipo FIFO (First In First Out). Es el algoritmo más sencillo y barato
de implementar, ya que sólo lleva una cola de las páginas a reemplazar, cuyo orden en
principio es aleatorio. Este esquema es el que, en general, produce más fallos de página, y
por lo tanto el menos eficiente.
b) Reemplazo óptimo. Es el mejor algoritmo en cuanto a la cantidad de fallos de página, pero es muy difícil de implementar. Se etiqueta cada página con un número equivalente a la
cantidad de instrucciones del programa en el que se hace referencia por primera vez a esa
página. El algoritmo implica cambiar las páginas de acuerdo al menor número, es decir, la
página a la que se hace referencia en la menor cantidad de instrucciones del programa. Para que este algoritmo pudiera ser implementado, el programa no debería tener saltos, o los
saltos ocurrir dentro de páginas contiguas a la actual, lo cual es irrealizable en programas
convencionales.
17
c) La menos recientemente usada (variable tiempo)(LRU: Least Recently Used). Es un
algoritmo que se basa en tiempo que ha pasado desde que esa página se usó. Es probable
que las páginas que han sido usadas recientemente, no se usen enseguida. La implementación de este algoritmo implica mantener una lista histórica de cuándo fueron usadas las
páginas solicitadas por cada proceso.
d) La que más recientemente se usó (variable tiempo)(Most Recently Used) El algoritmo
es el contrario al anterior y, a pesar de ello, es mejor que el de reemplazo sencillo.
e) Menos frecuentemente usada (variable frecuencia)(LFU: Least Frequently Used). El algoritmo anterior se basaba directamente en el tiempo transcurrido desde el último uso de
una página en particular. En este algoritmo, lo que se verifica es la cantidad de veces que
fue solicitada históricamente esta página. Ante un fallo de página, la que se reemplaza es
la que menos veces fue utilizada por el programa.
f) Más frecuentemente usada (variable tiempo)(Most Frequently Used) Como en el caso
de tiempos, este algoritmo implica considerar el cambio de la página que más se haya
usado. Teniendo en cuenta la localidad de las referencias, este algoritmo debería ser peor
que el anterior.
Existen otros métodos de intercambio de páginas más o menos eficientes que suponemos se tratarán con más detalle en la materia destinada a SO.
Caso práctico del uso de los algoritmos vistos
Tomaremos el caso de un proceso que utiliza secuencialmente las páginas siguientes:
7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2, 1, 0, 1, 7, 0, 1
Supondremos además que sólo disponemos de tres marcos para completarlos con los datos de
esos números de página.
FIFO
Hay 19 pedidos de páginas, a continuación se grafican los fallos de página.
Con este algoritmo, de 19 pedidos hubo 12 fallos. Tener en cuenta que esto es una cola en donde
los nuevos datos entran por la parte superior y los últimos datos se pierden.
ÓPTIMO
18
En este algoritmo se reemplaza la página que no se usará durante el mayor período de tiempo que
sigue, es decir, el SO debe “adivinar” la secuencia de páginas que usará el programa.
LRU
Este algoritmo se podría llevar a cabo asociando cada posición de la tabla de páginas a un contador que se vaya incrementando con unidades de tiempo predefinidas. La página que se cambia es
la que tiene el mayor número asociado. En el caso de no haber fallo de página, el contador asociado a ella se vuelve a 0.
LFU
En el ejemplo, al querer aplicar el algoritmo, nos encontramos con que dos de las páginas han
sido usadas igual número de veces, en este caso adoptamos cambiar la que más lejana en el tiempo estuvo, o sea que, en realidad, hemos hecho una mezcla de dos algoritmos.
Multiprogramación por segmentos
Conceptualmente se puede entender como un conjunto de espacios de longitud variable. La ventaja de esta imagen sobre el enfoque de paginación estriba en que se puede proporcionar protección a nivel de byte si fuera necesario. Para el Pentium existe una referencia al segmento de 16
bits y un desplazamiento de 32 bits. Dos de los bits de la referencia al segmento se utilizan para
protección y los 14 restantes para especificar el segmento. En total se dispone entonces de 246, o
19
sea 64 TeraBytes; el espacio de direcciones físicas utiliza 32 bits, lo que da una memoria de trabajo de 4 GigaBytes (límite de los SO actuales).
Algoritmos para el manejo de la memoria secundaria o virtual
Para mejorar la eficiencia de la transferencia de datos en el disco, se pueden usar 3 algoritmos
básicos:
a) FCFS (First Come First Served). El archiconocido método de que el primero que llega es
el primero que se atiende.
b) SSTF (Shortest Seek Time First). Con este algoritmo se organiza la cola de pedidos al
disco de manera que estén ordenadas por cercanía a la posición que se toma como inicial.
c) SCAN (Barrido). Este método trata de mantener la dirección (ascendente o descendente)
que llevaban las cabezas al momento que consideramos como inicial y lee sólo en esa dirección, cuando vuelve al principio no lee ni escribe.
d) C-SCAN (Barrido circular) Este algoritmo lee tanto en el camino de pistas ascendente
como descendente
Estos algoritmos tratan de minimizar las traslaciones entre pistas de las cabezas que era un tiempo relativamente largo y que afectaba fundamentalmente a la velocidad de transferencia de datos.
En el siguiente gráfico vamos a sacar los tiempos de respuesta suponiendo que un proceso haya
pedido datos de las siguientes pistas:
50, 98, 183, 37, 122, 14, 124, 65, 67
20
Memoria Caché
Glosario
Capacidad: normalmente se expresa en términos de Bytes, aunque dependiendo de la jerarquía
de memoria se puede expresar en palabras. Lamentablemente nunca se pusieron de acuerdo en
cuanto a la definición de cantidad de bits de una palabra. En general se toma como la unidad “natural” de la jerarquía de memoria de la que se está hablando, y de la CPU asociada. Hay palabras
de 32, 64, 128 bits.
Tiempo de acceso: es el tiempo que tarda en realizarse una operación de lectura/escritura completo, es decir: el tiempo que transcurre desde el instante en que se presenta una dirección a la
memoria hasta que el dato (Byte o palabra) está disponible para su uso en el bus de datos.
21
Tiempo de ciclo de memoria: es el tiempo de acceso más los tiempos de las señales de control
involucradas.
Ubicación: indica si la memoria es interna o externa al computador. La memoria interna suele
identificarse con la memoria principal (RAM). El punto de vista para determinar la ubicación
podría ser también la CPU; en este caso la memoria interna estará constituida por los registros y
la memoria caché, y la memoria externa pasaría a ser la RAM.
Unidad de transferencia: indica cuántos bits o Bytes se transfieren en un ciclo de lectura/escritura. Algunas veces coincide con la palabra, pero en otros casos es mayor (256 bits o
más).
Unidades direccionables: en la mayoría de los casos, coincide con la palabra; en algunos, especialmente los registros, se permite direccionar a nivel de bits. En cualquier caso, la relación entre
las unidades direccionables A y el número de unidades direccionables es 2A=N.
Velocidad de transferencia: es la velocidad a la que se pueden transferir datos (actualmente se
habla de bits por el tipo de transmisión usado). Se puede utilizar la siguiente relación:
TN  TA 
N
R
TN = tiempo medio de lectura/escritura de N bits.
TA = tiempo de acceso medio por bit
N = número de bits
bits
R = velocidad de transferencia en
seg
Jerarquía de memoria
Memoria en
tarjetas/CPU
Registros
Alto costo
por bit
Muy baja
capacidad
Muy rápida
Accesos
CPU a L2
Muy bajo
costo por bit
Alta capacidad
Lenta
Accesos
CPU a L1
Caché
RAM
Almacenamiento
Discos
fuera de tarjetas
Almacenamiento
WORM
fuera de línea
Existe un compromiso entre las tres características clave (costo, capacidad y tiempo de acceso).
A menor tiempo de acceso, mayor costo; a mayor capacidad, menos costo por bit; a mayor capacidad, mayor tiempo de acceso.
Existe una solución de compromiso que implica tener memorias en diferentes “niveles” (Levels),
estos se enumeran con valores en incremento de acuerdo a la lejanía relativa a la CPU; así el L1
es la memoria más cercana a la CPU y la L3 la más lejana. Este esquema implica tener varios
niveles ordenados por capacidad-velocidad; en este caso, L1 es la más veloz, aunque de menor
capacidad.
22
La última columna de la tabla de jerarquías supone que los acccesos a memoria lenta son cada
vez menos frecuentes. Esto es moderadamente verdad, siempre que se cumpla la condición de
que la ejecución de un proceso sea efectuado genéricamente en pocas páginas de instrucción (o
datos); con la disminución de los costos de caché al ser integrados al chip de la CPU esta condición es mucho más frecuente y por lo tanto la eficiencia del uso de caché se nota mucho más.
Esta característica de los programas o procesos fue denominada localidad de las referencias
(temporal y espacial) y establece que durante períodos de tiempo largos (tiempos de CPU), las
agrupaciones de memoria en uso cambian, pero considerando períodos cortos, el proceso tiende a
usar las mismas referencias a memoria. Estudios realizados sobre programas realizados en C demuestran que con 8 bloques de caché por proceso, sólo hay fallos de página en menos del 1% de
los accesos a caché.
En el cálculo de los tiempos de acceso para memorias de dos niveles, debemos considerar el concepto de tasa de aciertos. En una configuración de dos niveles, la tasa de aciertos de un nivel
corresponderá a la inversa (Tasa de fallos) del otro nivel.
TM  H * T1  (1  H ) * (T2  T1 )
TM  T1  T2 * (1  H )
H= tasa de aciertos del nivel 1
T1= tiempo de acceso medio al nivel 1
(1-H)= tasa de fallos del nivel 1 (certeza – aciertosL1 = fallosL1)
T2= tiempo medio de acceso al nivel 2
TM= tiempo medio de acceso a los dos niveles
Si H fuera el 100%, el tiempo medio sería T1; si H fuera 0, el tiempo medio sería T1+T2
Supongamos que el procesador tiene que acceder a dos niveles de memoria: L1 contiene 1K palabras y un T1 = 10 nseg.; L2 contiene 100K palabras y T2 = 100nseg. y H = 0,95. El método de
escritura de la caché es WT (write through), lo que lleva a considerar que si debe acceder al L2
debe haber efectuado un ciclo de lectura de L1 para buscar la página y, al no encontrarla, busca
en L2; cuando la encuentra debe grabarla en L1 para que la CPU pueda acceder al dato (o instrucción en V.Neumann). Despreciando los tiempos de handshaking, el tiempo medio de acceso
será:
0,95 *10nseg  (1  0,95)(10nseg  100nseg )  15nseg
Lo que justifica usar diferentes niveles son las condiciones implícitas en el gráfico de jerarquías:
L1 pequeña pero extremadamente veloz, L2 más grande y más lenta y L3 mucho más grande y
aún más lenta que L2. Una relación posible es un orden de magnitud (10 veces) entre niveles.
Prestaciones
Para evaluar la cantidad de caché a utilizar se deben considerar el costo y el rendimiento final del
conjunto. El costo se calcula como:
Ct 
C1M 1  C2 M 2
M1  M 2
C es el costo por palabra
M es la cantidad de palabras a adquirir
23
El nivel 2 es más barato, de modo que interesa conseguir que Ct sea muy próximo a C2. Esto se
consigue haciendo que M1 << M2, cosa que se cumple en la realidad. También debemos conseguir que TM sea cercano a T1, lo que se consigue con una caché de gran tamaño.
Se deben considerar las dos variables: tiempo de
acceso (función de los aciertos) y costo (función
del tamaño). Sabiendo que la relación de los
tiempos de acceso medio entre la memoria principal y la caché es un número cercano a 50 y que
una relación entre tiempo medio del sistema y
tiempo de acceso a caché puede ser 10, obtenemos una tasa de aciertos mínima del 82%. La
cuestión de la cantidad será función de la localidad de las referencias a memoria, para una localidad entre moderada y fuerte conseguimos tasas
de acierto en ese orden con un 20% de memoria
caché.
Comprobar si se cumplen estas condiciones
usando la memoria principal como caché de disco, suponiendo que la relación de costos es 1/200 y la relación de tiempos 1000/1.
El cálculo se complica un poco al considerar tres niveles, pero se puede resolver de a dos sistemas por vez, como un sistema lineal.
Partimos de que tenemos la certeza de que la página buscada está en alguno de los tres niveles:
H1  H 2  H 3  1
Entonces, el tiempo medio (o promedio) de acceso queda:
t m  H1 * t1  H 2 * t1  t 2   H 3 * t1  t 2  t 3 
Reagrupando:
tm  tt H1  H 2  H 3   t2 H 2  H 3   t3 H 3
H1  H 2  H 3  1
tm  tt  t2 1  H1   t3 1  H 2  H1 
En la actualidad, tenemos microprocesadores con hasta 3 niveles de caché integrados dentro de la
CPU, esto permite velocidades en el orden (o iguales) a la del reloj que comanda a la CPU. El
problema de la cercanía es un tema físico: si la longitud de la línea(s) de transmisión de datos se
asemejan a la longitud asociada a esa onda, el conductor se transforma en una antena que irradia
energía al exterior como ondas electromagnéticas (el principio de la radiodifusión), concretamente para frecuencias del orden de los 2GHz, la longitud de onda se calcula como:
 m 
3.108 

c
 seg   1,5.10 1 m  15cm
 
f
2.109 Hz
24
Para que no oficie de antena, la longitud de la línea debe ser alrededor del 10% de  , o sea 1,5
cm. Resumiendo lo anterior: no se deben mover señales eléctricas con frecuencias cercanas a los
2GHz a distancias mayores de 1,5cm.
La figura siguiente describe la estructura de un sistema de memoria caché/principal. La memoria
principal consta de hasta 2n unidades direccionables y cada palabra tiene una única dirección de
m bits. La memoria está dividida en bloques (podrían ser páginas) de K palabras por bloque, es
decir que hay M bloques (páginas), siendo M = 2n/K. La caché consta de C filas, cada fila contiene K palabras, es decir que en cada fila de la caché entra un bloque de los considerados para la
memoria; tiene además unos bits de más como etiqueta, que habitualmente es parte de la dirección del bloque e información sobre modificaciones en el bloque (si fue modificado por el proceso hay que grabarlo a disco antes de eliminarla de la caché, en caso contrario sólo se elimina). Se
cumple la condición de que C<<M. En todo momento un subconjunto de bloques de la memoria
está copiado en la caché.
Unidades de Entrada/Salida
Uno de los elementos clave de un computador es el conjunto de módulos de Entrada/salida. Cada
módulo se conecta al bus del sistema o a un conmutador central y controla uno o más dispositivos
periféricos. Las razones por las cuales un periférico no se conecta directamente al bus son, entre
otras:
a) Existe una amplia variedad de periféricos con lógicas de funcionamiento diferentes
b) A menudo la velocidad de transferencia de datos es mucho menor que la del bus
25
c) Algunos pocos periféricos pueden tener velocidades que superen a la del bus
d) Los periféricos usan datos con formatos y tamaños de palabra diferentes a los del computador.
Como interfase entre el bus y el dispositivo genéricamente aparece un módulo de entrada/salida.
Este módulo, como función, debería compensar la lógica del dispositivo de entrada/salida, los
formatos y las velocidades. Las funciones de un módulo suelen incluir, además de las antedichas,
el almacenamiento temporal de datos y la detección de errores.
El control de la transferencia de datos desde un dispositivo externo hasta el procesador podría
involucrar los siguientes pasos:
a) El procesador interroga al módulo de entrada/salida para comprobar el estado del dispositivo.
b) El módulo devuelve el estado.
c) Suponiendo que el dispositivo está preparado para transmitir o recibir datos, el procesador
solicita al módulo la transferencia del dato.
d) El módulo de entrada/salida obtiene o transmite el dato del dispositivo externo.
e) El dato se transfiere desde el módulo al procesador.
Con referencia al gabinete, los dispositivos pueden dividirse en internos y externos. Genéricamente, los externos interaccionan con el operador y, a excepción del monitor (video), se pueden
considerar como muy lentos. Los internos están más relacionados a los datos y sus velocidades de
transferencia, comparados con los de la CPU, también son lentos (pero mucho más rápidos que
los externos).
Como ejemplo, citaremos el módulo (chip) identificado como 8255, destinado a controlar interfases que manejan datos en forma paralela (8 bits). Este chip es bastante parecido a un microcontrolador: tiene tres puertos de 1 Byte, configurables como entradas o salidas, una salida/entrada de
datos de 1 Byte, algunas señales de control y dos líneas para direcciones.
El uso más difundido fue el concepto de “mapeado en memoria”. Este concepto implica que los
registros internos del módulo (en este caso, el 8255) se hacen coincidir con direcciones físicas de
la RAM. Con este método, las diferencias de velocidad se solucionan porque esas posiciones de
26
memoria RAM actúan como un almacenamiento de datos temporal (buffer) que compensa las
diferencias de velocidad.
En el modo de transmisión, la CPU coloca el dato a transferir en la posición de RAM asignada,
habilita al módulo con una señal de control denominada Chip Select (CS) y, a continuación, otra
señal de control denominada Write. En el ciclo de lectura, el chip coloca al dato en otra posición
de memoria, solicita una interrupción y, cuando ésta es aceptada, la CPU le indica al módulo que
está lista a leer el dato con la señal Read; esta última aparentaría no ser necesaria, ya que la CPU
puede leer el dato estrictamente con un acceso a memoria, esta señal, en realidad, le sirve al módulo para saber que el dato fue leído y que, eventualmente, puede colocar otro dato en la memoria.
Este módulo, en particular, es programable en una cantidad de modos de funcionamiento. Esos
diferentes modos se eligen a través de un dato denominado “Palabra de control”, al que se le
asigna otra posición de memoria, efectuándose la programación a través de un ciclo de escritura y
el conocimiento del estado del chip a través de uno de lectura.
El método anterior, denominado E/S programada tiene como ventaja la sencillez de la implementación, pero la desventaja de emplear mucho tiempo en ciclos de lectura/escritura de RAM para
obtener el/los datos necesarios. Como ejemplo, supongamos que el proceso en ejecución requiere
como operando a un valor codificado en PuntoFlotanteDoblePrecisión; esto requiere leer 64 bits
(8Bytes) y también 8 ciclos de lectura del módulo de E/S.
Mientras la CPU carga el/los datos necesarios no puede hacer otra cosa que esperar respuesta del
módulo. Como las velocidades de E/S son considerablemente menores que la RAM y la CPU, la
solicitud y carga del dato requiere mucho tiempo de inactividad de la CPU. En computadoras
personales, esto es un inconveniente menor ya que se resume a que el operador obtenga una baja
velocidad y use el tiempo de
inactividad de su CPU en penControladores
sar cómo puede hacer para
de interrupciones
esclavos
comprarse una PC más rápida.
Si cambiamos nuestro punto de
Dispositivo externo 00
IRQ 0
vista a los negocios, ese tiempo
IRQ
1
Dispositivo externo 01
INT
representa dinero quieto en funIRQ 2
Dispositivo externo 02
IRQ 3
ción de dinero moviéndose y,
IRQ 4
por supuesto, el dinero moviénIRQ 5
dose genera ganancias así que
IRQ 6
hay que hacer más eficiente al
Dispositivo externo 07
Controlador
IRQ 7
de interrupciones
sistema.
Dispositivo externo 08
Dispositivo externo 15
Dispositivo externo 56
Dispositivo externo 63
IRQ 0
IRQ 1
IRQ 2
IRQ 3
IRQ 4
IRQ 5
IRQ 6
IRQ 7
IRQ 0
IRQ 1
IRQ 2
IRQ 3
IRQ 4
IRQ 5
IRQ 6
IRQ 7
maestro
INT
IRQ 0
IRQ 1
IRQ 2
IRQ 3
IRQ 4
IRQ 5
IRQ 6
IRQ 7
INT
INTR
CPU
Para mejorar este rendimiento,
se pensó en agregar un controlador llamado de interrupciones
y hacer los pedidos a través de
él. En este esquema, cuando la
CPU necesita un dato ejecuta
una orden de lectura a una dirección. La línea de lectura
puede ir directamente al controlador de interrupciones o al
INT
27
controlador y a la RAM. La CPU sigue con otras tareas mientras el controlador de interrupciones
prepara al módulo de E/S correspondiente a la dirección suministrada por la CPU. Cuando el sistema de E/S (módulo + hardware correspondiente) está listo, lo comunica al controlador de interrupciones (CI) por una línea de control de las 8 que dispone el CI. Dependiendo de la línea solicitante, el CI las ordena por prioridades y solicita a la CPU la interrupción por la línea IntR, junto
con la identificación del periférico que requiere atención. El SO determina si se va a atender o no
esa interrupción a través de “máscaras” de comparación. Las “máscaras” no son otra cosa que
datos contra los cuales se compara la identificación del periférico que requiere atención con algún
orden de prioridad preestablecido.
El 8259 de Intel (CI) se puede cablear en modo árbol para manejar hasta 64 dispositivos de E/S.
La programación normal determina que la línea IRQ 0 es más prioritaria que la INT 7, por lo tanto en la figura tienen prioridades relacionadas en forma inversa a la identificación del dispositivo.
En el caso de que la interrupción sea aceptada, el SO entra en un ciclo de cambio de proceso
guardando los registros de la CPU y los datos necesarios para retomar el proceso actual y cuando
están preservados, solicita al CI la dirección correspondiente del vector de interrupciones para
seguir el proceso de lectura/escritura del dato.
Aparentemente todo este proceso es más largo que el anterior y es cierto, pero usando interrupciones, la CPU puede hacer otras cosas entre el pedido del dato y la lectura del mismo. El resultado es un aumento en la eficiencia de la CPU y por ello de todo el conjunto.
El manejo de E/S por interrupciones no soluciona el problema de tener varios ciclos para leer un
dato compuesto de varios Bytes, una alternativa más moderna es usar una CPU ayudante denominada Direct Memory Access (DMA). Este chip se interpone en la comunicación de los datos
con periféricos que habitualmente requieren datos largos y tiempos de respuesta menores a sus
transferencias a la memoria RAM, como por ejemplo la placa de red, la controladora de sonido y
el disco rígido, entre otros posibles.
La ventaja de agregar esta CPU ayudante es que la CPU principal le comunica el tipo de dato que
necesita y la dirección inicial de memoria en que hay que ubicarlo. El DMA se encarga de la tarea rutinaria de ir cargando las posiciones consecutivas de memoria hasta completar el dato y
avisarle a la CPU la disponibilidad del mismo a través de una interrupción. En este procedimiento, la CPU sólo actúa para pedir el dato y para obtenerlo.
En el caso de existir un solo Bus de Datos, van a tener que compartirlo entre la CPU y el DMA,
lo cual hace que la eficiencia del sistema se incremente comparado al uso sin DMA, pero no lo
deseable. La alternativa más costosa es construir otro Bus que se llamará de E/S conectado directamente al DMA, con lo cual los procesamientos de E/S se delegan al DMA y los procesos más
rápidos a la CPU, cada una de ellas con su propio canal de comunicaciones.
El último concepto relacionado con las operaciones de E/S es el denominado “Canal de E/S”. El
módulo de E/S tiene una zona de memoria propia, Buses propios y maneja un repertorio reducido
de instrucciones (es capaz de ejecutar procesos) tendientes a las comunicaciones con los dispositivos de E/S, desde un punto de vista es otra computadora cuya tarea es comunicar datos a una
computadora más rápida (esquema de cliente-servidor). El rendimiento, la amplitud de datos y la
seguridad obtenida en el uso de la WideWorldWeb (internet) hizo que se pensara en esquemas
distribuidos aún dentro del mismo gabinete y ahora existen configuraciones de E/S (FireWire,
Infiniband, USB) que usan protocolos de comunicaciones similares a los usados en Internet, aunque con menos capas y mucho más rápidos, llegándose en algunos casos de uso de fibra óptica a
28
velocidades de transferencia de 30 Gb/seg y a 300m de distancia, cuando por cobre el PCI llega a
1Gb/seg a distancias de pocos centímetros.
Interrupciones en el Pentium
Hay dos tipos de eventos que hacen que el Pentium suspenda la ejecución del flujo de instrucciones en curso y responda al evento: las interrupciones y excepciones. Una interrupción se genera
por una señal del hardware y puede ocurrir en cualquier momento. Una excepción se genera desde el software y es provocada por la ejecución de una instrucción. Hay dos fuentes de interrupciones y dos fuentes de excepciones.

Interrupciones enmascarables: las recibe el procesador por el pin INTR. El procesador no
atiende una interrupción enmascarable a no ser que un indicador especial lo habilite.

Interrupciones no enmascarables: el pin se denomina NMI. El procesador no tiene forma
de evitar atender a tales interrupciones (por ejemplo: RESET).

Excepciones detectadas por la CPU: se producen cuando el procesador encuentra un error
mientras intenta ejecutar una instrucción.

Excepciones programadas: hay instrucciones que generan una excepción (por ejemplo: fallo de página, depuración, etc.)
El procesamiento de las interrupciones usa una tabla de vectores, cada tipo de interrupción tiene
asignado un número que se usa para identificarla. Este número se usa como entrada a la tabla de
vectores de interrupción. La tabla contiene 256 datos de 32 bits cada uno, que representan la dirección (segmento y desplazamiento) de la rutina de servicio a ejecutar. Si se generan más de una
interrupción o excepción, la CPU las atiende en un orden determinado por el SO.
ID
0
1
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
DESCRIPCIÓN
Error al dividir; desbordamiento de división o división por cero
Excepción de depuración. Incluye fallos e interceptaciones relacionadas con la depuración de procesos.
Punto de parada causado por la instrucción INT3
Desbordamiento detectado por INT0
Instrucción fuera de límites de la memoria
No definido
Dispositivo no disponible
Doble fallo: dos interrupciones ocurres durante la misma instrucción
Reservado
Segmento en estado de tarea no válido
Segmento no presente
Error en la pila
Violación de protección general
Fallo de página
Reservado
Error de coma flotante
Acceso a una palabra almacenada en dirección de Byte impar o doble palabra en
una dirección que no sea múltiplo de 4
Verificación de la CPU (Watchdog)
29
19-31
2
32-255
Reservados
Interrupción no enmascarable
Vectores de interrupción del usuario (señal INTR)
Las dos últimas son interrupciones y el resto excepciones. Cuando se produce una interrupción y
es atendida tiene lugar la siguiente secuencia de eventos:
1) Si la transferencia supone un cambio de nivel de privilegio, los contenidos del registro del segmento de pila y puntero de pila ampliado se introducen en la pila.
2) El valor actual del bit EFLAGS se introduce en la pila.
3) Los indicadores de interrupciones y de trampa se ponen a cero. Ello inhabilita
otras interrupciones y la interceptación o modo paso a paso.
4) Los contenidos actuales del puntero del segmento de código y del puntero de instrucciones se guardan en la pila.
5) Si la interrupción viene acompañada de un código de error se guarda también en la
pila.
6) Se captan los contenidos del vector de interrupción y se cargan los registros necesarios para la ejecución de la interrupción.
7) La instrucción IRED vuelve a cargar los registros desde la pila para continuar la
ejecución anterior a la interrupción.
Paralelismo en las instrucciones – procesadores superescalares
El término “superescalar” fue usado en 1987 para mencionar a una máquina diseñada para mejorar la velocidad de ejecución de instrucciones escalares y estuvo muy relacionado con los procesadores pensados en esa época, en donde había más limitaciones en la construcción de grandes
memorias RAM (internas y externas). Esta limitación llevó a diseñar procesadores que tenían
pocas instrucciones diferentes, pero cuya ejecución se podía decodificar y ejecutar en tiempos
muy cortos, dándoles el nombre de RISC (conjunto de instrucciones reducido).
En la actualidad, un procesador superescalar es aquel que usa múltiples cauces de instrucciones
independientes. Cada cauce consta de múltiples etapas, de modo que puede tratar varias instrucciones a la vez (típicamente un programa de computadora debe considerar que tiene sus instrucciones ordenadas en una serie cuya lógica resuelve el problema). El hecho de que haya varios
cauces introduce un nuevo nivel de complejidad al permitir varios flujos de instrucciones que se
procesan simultáneamente. El procesador superescalar saca provecho de lo que se conoce como “
paralelismo en las instrucciones”, que hace referencia al grado en que las instrucciones pueden
ejecutarse en paralelo (independencia de las instrucciones).
El procesador capta varias instrucciones a la vez y trata de encontrar instrucciones cercanas que
sean independientes entre sí y puedan, por ello, ejecutarse en paralelo (multiplexión de recursos,
no del tiempo). Si la entrada de una instrucción no es independiente, esto es que su entrada de
datos depende de la salida de otra (precedente), esta instrucción no puede ejecutarse al mismo
30
tiempo o antes de aquella. Una vez que se han identificado tales dependencias, el procesador
puede emitir y completar la ejecución de instrucciones en un orden diferente al código original.
El procesador puede eliminar algunas dependencias innecesarias a través del uso y renombramiento de algunos de su gran cantidad de registros internos.
Para mejorar el valor del “paralelismo de las instrucciones” se puede usar una combinación entre
software (el compilador) y hardware. Algunas de las limitaciones que se encuentran para mejorar
ese valor son:

Dependencia de datos verdadera

Dependencia relativa al procedimiento

Conflicto de recursos
Dependencia de datos verdadera:
Supongamos el ejemplo
A=A+B
C=A
La segunda instrucción no puede ejecutarse antes (o durante) la ejecución de la primera porque
necesita datos de ella. Los procesadores superescalares pueden atrasar un ciclo de reloj a la instrucción dependiente para poder ejecutarla. Esto, de alguna manera es una multiplexión en tiempo, es decir que una instrucción se ejecuta después que la otra.
Dependencia relativa al procedimiento
El caso de una bifurcación condicional es una dependencia relativa al procedimiento, mientras no
se sepa el valor de la condición no se puede ejecutar la instrucción de salto.
Conflicto de recursos
Uno de los recursos que aparenta ser poco importante son las comunicaciones a través del bus de
datos, si la máquina tiene uno solo, las comunicaciones terminan siendo un cuello de botella para
el requerido paralelismo. Este conflicto se resuelve parcialmente disponiendo de varias computadoras virtuales (o reales) en una configuración de red, siempre y cuando la comunicación de
esta red sea mucho más veloz que las comunicaciones internas de datos de cada nodo.
Implementación superescalar
31
La figura representa gráficamente el proceso de ejecución de instrucciones en una CPU de este
tipo.
El programa a ejecutar consiste de una secuencia de instrucciones, tal como sale del compilador.
El proceso de captación de instrucciones tiene una lógica para predecir saltos y genera un flujo
dinámico de instrucciones que envía a una ventana de ejecución. En esta ventana, las instrucciones no forman un flujo secuencial sino que están estructuradas de acuerdo a sus dependencias de
datos verdaderas. Aquí podrían aparecer instrucciones “fantasma”, necesarias para completar una
lógica de programación crítica (PERT). Las instrucciones se ejecutan y se desechan los resultados
irrelevantes o de tareas fantasma y se reordena secuencialmente el flujo de salida.
Los procesadores de Intel han ido modificando su estructura de escalar (80486), modesta arquitectura superescalar (Pentium: dos unidades independientes ALU para enteros), hasta ser completamente superescalares (Pentium Pro).
Arquitectura IA-64
Esta soporta desde el hardware el paralelismo de instrucciones y es bastante diferente a las aproximaciones seguidas por la historia de lo superescalar. Como siempre, está acompañada de la
tecnología que permite grandes cantidades de registros y caché de hasta tres niveles integradas
dentro del chip. Tiene múltiples unidades de ejecución completas, siendo hasta ahora cuatro, se
piensa que es posible llegar a ocho.
Un resumen que no expresa todo su potencial es que ahora las instrucciones se ejecutan con predicado, esto es que los ciclos de saltos condicionales se ejecutan completos: ambas ramas llegan a
sus resultados y luego se comprueba cuál de las ramas ejecutadas sirve como dato, la otra se
desecha.
La teoría de hacer y luego comprobar se aplica también en el caso de instrucciones de control y
datos.
Referencias bibliográficas:
32
Sistemas Operativos Modernos, Adrew S. Tanenbaum. Editorial Prentis Hall.
Organización y Arquitectura de Computadores, William Stallings. Editorial Prentis Hall.
33
Programación con assembler de un PIC 16F628/872
Los genéricamente llamados procesadores tienen dos ramas que difieren en el modo en que se
comunican con el exterior: los procesadores propiamente dichos tienen patas definidas para entrada, salida, datos, direcciones y alimentación conocidas y fijas en cuanto a sus funciones. Las
comunicaciones con el exterior son síncronas y permanecen en un estado lógico tiempos muy
cortos, en algunos casos la duración de una instrucción (en el orden de algunos nanosegundos),
debido a la gran cantidad de información que se espera que manejen.
Los procesadores que llamaremos “controladores” se diferencian por tener patas de comunicación
que pueden variar sus funciones durante la ejecución de un programa. Pueden ser durante unos
ciclos de reloj entradas, luego pasar a ser salidas e incluso permanecer en un estado lógico inamovible durante mucho tiempo. El procesador al cual nos dedicaremos pertenece a esta última
clase, lo fabrica la firma Microchip y llevan genéricamente el nombre de PIC y el núnero de modelo del que se trate, en este caso el 16F628.
El primer número hace referencia al grupo dentro de la clase: existen los 12, 14, 16 y 18, cuanto
mayor el número, mayor la potencia de procesamiento. A excepción de los de alta gama, todos
tienen un grupo reducido de instrucciones (RISC), que ejecutan en lo que se denomina “ciclo de
instrucción” y que lleva cuatro ciclos del reloj.
Con estos controladores se han fabricado desde boyas hasta la lógica de un automóvil. Son muy
versátiles en cuanto a la frecuencia de reloj y la cantidad de patas de comunicación con el exterior, además de las funciones internas que son capaces de manejar que cubren desde comunicaciones seriales, paralelas, conversores analógico-digitales, conversores de frecuencia, modulación, etc. etc.
Todos tienen la esctructura de una máquina de Harvard, es decir una memoria (más el bus correspondiente) para instrucciones y una memoria para datos, esto le permite instrucciones de 14 bits
contra 8 bits de la memoria de datos.
El modelo elegido tiene capacidad de almacenar programas de hasta 2000 líneas en una memoria
tipo Flash (de allí la F del nombre), una RAM organizada en cuatro bancos de 128 posiciones
cada uno en donde residen los registros de control y algunas posiciones para ocupar con variables
del programa y una zona de memoria EEPROM (64 Bytes) en donde colocar datos cuyos valores
se quiere preservar ante una falta de energía eléctrica (valores de configuración o promedios,
etc.). El chip tiene 28 patitas, 24 son de entradas/salidas organizadas como tres grupos (PORTS)
de 1 Byte y las restantes son para la alimentación y reloj externo. Cada pata tiene la posibilidad
de ser entrada o salida digital o analógica, entrada de eventos exteriores que se pueden contar,
entrada del programa, interrupción, etc. Como promedio cada pata puede tener dos funciones
programables desde el programa a ejecutar (cambiables en tiempo de ejecución). Está dotado de
tres contadores identificados como TMR0, TMR1 y TMR2, hasta ocho interrupciones concatenadas, 10 eventos diferentes para provocar inerrupciones, conversor A/D de 10 bits, comunicación
tipo Master o Master/Slave y un circuito de detección de fallos en la alimentación eléctrica que
permite pasarlo al modo “sleep”, de bajo consumo y conservación de los datos (debe tener una
alimentación extra por batería tipo litio).
Sobre la familia de controladores PIC existe una infinidad de documentación, programas, ensambladores y simuladores que dejan a cualquier apunte como el presente como una simplísima introducción al tema. Es justamente eso lo que pretende este apunte: hacer una introducción al lenguaje ensamblador de programación.
34
Los lenguajes de programación tratan de simplificar la vida del programador a medida que incrementan su nivel. El nivel de máquina, donde hay que escribir cada instrucción con los códigos
de operación y los argumentos en secuencias de unos y ceros, resulta ser el más complicado; a
este nivel le sucede el assembler, en donde cada instrucción se identifica por una cantidad de letras (tres o cuatro) y los argumentos escritos en numeración decimal o hexadecimal; a estos le
suceden los de mayor nivel, en donde la programación ya acepta lenguajes matemáticos y sintácticos simples, cuyo parecido al lenguaje convencional es cada vez mayor.
El reloj
Tiene un circuito interno que puede oficiar de reloj con sólo dos componentes externos que pueden oscilar entre 100KHz y 1MHz aproximadamente, este reloj sencillo es poco preciso, variando
con la tensión de alimentación, la temperatura, la variación de valor de los componentes, etc. Para
osciladores más precisos se deben usar cristales y se pierden dos patas del controlador. Este controlador puede funcionar con frecuencias de reloj desde 0Hz (parado) hasta 20 MHz. Estos extremos de frecuencias darían tiempos de instrucción entre infinito y 200 nanosegundos.
Como reloj interno para contar tiempos se usa el tiempo de instrucción que equivale a cuatro ciclos del reloj.
Timer cero (TMR0)
Como habíamos dicho antes, los temporizadores son, en realidad, contadores de eventos. Si estos
eventos se realizan en tiempos iguales, la cuenta de estos implicaría un tiempo conocido.
Este timer en particular se puede configurar como contador de eventos externos, que entrarían
por una de las patas, o contador de eventos internos, provenientes de los tiempos de instrucción.
También puede programarse lo que se denomina “prescaler”, que es en realidad un divisor programable en 8 etapas que van desde 1:2 a 1:256. El registro usado para poder configurar los
tiempos es el TMR0, de 8 bits, lo que permite divisiones de hasta 256 en pasos de a 1. La máxima
división que obtendríamos usando el prescaler y el registro es de 1:65536.
La salida es un bit (flag) del registro INTCON, denominado TMR01F (bit 2 del INTCON). Este
flag, que habitualmente está en 0, cambia a 1 cuando el registro TMR0 pasa del valor FFh a 00h
(OVERFLOW). Si se configuran las interrupciones, este temporizador puede generar en el overflow una interrupción.
Timer uno (TMR1)
Las diferencias con el timer 0 son las siguientes: el prescaler tiene 4 rangos configurables de 1:1 a
1:8, el registro TMR1 pasa a ser de 16 bits, pudiendo cargarse en 2 partes como TMR1L y
TMR1H. El timer este no genera interrupciones y su cuenta máxima será de 524288
Timer dos (TMR2)
Muy parecido a los otros dos, salvo por la estructura, que en este caso tiene un prescaler configurable entre 1 y 16 en tres pasos, el registro TMR2 de 8 bits y un postscaler configurable entre 1 y
3, también en tres pasos. Este Timer puede generar interrupciones y su cuenta máxima será por
12288.
PORTA
Tiene 6 patitas configurables como entradas digitales o analógicas o salidas digitales, esto se realiza a través del registro TRISA (bits 0 al 5) y se configuran con el ADCON1 en lo referente a
digitales/analógicas.
35
PORTB
Tiene 8 patitas configurables como entradas/salidas en ambos casos digitales. Se pueden habilitar
unas resistencias de “pull up” internas, con lo cual el estado lógico sin conexión al exterior serán
1’s. Otra característica muy interesante de este puerto es que las patitas RB7:RB4 se comparan en
cada instrucción con el valor anterior y, de haber cambios, puede generar una interrupción.
PORTC
Compuesto de 8 patitas bidireccionales configurables. Este, a diferencia del anterior, no puede
generar interrupciones y a diferencia del PORTA las entradas/salidas son siempre digitales.
INTERRUPCIONES
Todos estos controladores tienen la capacidad de manejar interrupciones, si bien mucho más simples que lo explicado para microprocesadores. El modelo que estamos estudiando acepta diez
causas de interrupción, de las cuales sólo nos dedicaremos a tres:
a) Cambios en las entradas de mayor peso en el puerto B (bits 7 a 4)
b) Overflow en el registro del Timer 0
c) Oiverflow en el registro del Timer 1
d) Overflow en el registro del Timer 2
El bit 7 del registro INTCON, denominado GIE (Global Interrupts Enable), habilita (en 1) las
interrupciones o las inhabilita (en 0), más allá de que estén habilitadas por los bits correspondientes a cada una de ellas. El controlador, cuando acepta una interrupción, guarda el contenido del
PC (program counter) en una zona de memoria RAM e inhabilita las interrupciones cambiando
de valor a GIE; es decir que sólo puede atender a una interrupción por vez. Al volver del programa de atención a la interrupción con RETFIE, habilita nuevamente las interrupciones y carga el
PC con el valor guardado en memoria, así puede continuar con la ejecución del programa principal.
Hay que tener presente que el controlador no guarda otros valores de registros que no sea el PC,
si el procesador necesita otros valores de configuración o registros (sobre todo el W) que pudieron ser modificados por la subrutina de interrupción, corresponde al programador incluir en esa
subrutina de atención, loos pasos necesarios para guardar valores importantes del programa principal. Un ejemplo de las instrucciones necesarias para esta operación está explicitada en el ejemplo de Tips & Tricks, más adelante en este apunte.
Además del GIE deben estar habilitados los flags correspondientes a cada una de las interrupciones que queramos atender:
a) Se habilita con el flag RBIE (bit 3 del INTCON)
b) Se habilita con TMR0IE (bit 5 del INTCON)
c) Se habilita con TMR2IF (bit 1 del PIR1)
Como el controlador tiene un solo vector de interrupciones (ubicado en 04h), desde esta dirección
debemos ir a una primera subrutina en la que determinaremos cuál es el flag que nos indica la
36
causa de la interrrupción y desde allí redireccionar a la subrutina de atención de esa interrupción
en particular. La primera instrucción debe cambiar el flag de la interrupción para evitar recursividad en la misma.
Alguien podría suponer que la rutina a ser atendida puede seguir a la dirección 04h y es cierto, lo
que sucede es que si por algún motivo hay que modificar esa rtuina, debe correrse todo el programa principal para hacer lugar a las instrucciones nuevas. Como la compilación se hace con
direcciones absolutas hay que recompilar todo nuevamente.
Como regla general, para programar en assembler conviene dividir el problema en partes e ir implementando cada una con una subrutina de pocas instrucciones que probaremos y repararemos
más fácil que encontrar el problema en un programa extenso. No siempre se puede, pero conviene
tratar de hacerlo. Una vez que se tienen las subrutinas probadas, el programa principal va llamando en el orden previsto a cada una de ellas para obtener un resultado (función) o para trabajar
variables o tiempos sin necesitar un resultado numérico (subrutinas).
SET DE INSTRUCCIONES Y ENSAMBLADOR (MicrochipASseMbler)
***En el Apéndice A se puede ver la configuración paso a paso del MPLab.
Un programa para este controlador tiene habitualmente tres partes.

La primera, que es la configuración del ensamblador, tiene las siguientes instrucciones:
a) RADIX: le indica al ensamblador en qué tipo de numeración se va a trabajar por defecto,
y pueden ser DEC, numeración decimal, HEX, numeración hexadecimal, o BIN, numeración binaria. Lo normal es que el ensamblador entienda numeración hexadecimal, si durante el transcurso del programa queremos poner valores en otra numeración bastará anteponerle al valor las letras d, h o b, y el valor entre comillas simples.
b) ORG: le indica al ensamblador el número de la línea en la que debe comenzar el programa. En estos controladores es común encontrar la instrucción ORG 5, que significa que la
primera línea de programa va a estar en la dirección 5, y esto a su vez, indica que el programa no va a usar las interrupciones cuyo vector está en la dirección 4 de memoria de
programa.
c) EQU: asigna a una etiqueta un valor que se tomará como la dirección de esa etiqueta. Es
cómodo asignarle a los registros más usados la dirección que efectivamente tienen en
memoria para recordar solamente el nombre del registro en operaciones de lectura o escritura.
d) LIST: permite leer un archivo de texto cuya extensión debe ser .inc y que contiene todas
las asignaciones de registros y memoria libre para evitarnos el cometer errores de programación.
e) START / END: es la instrucción de comienzo/finalización de una compilación. Habitualmente ubicada antes/después de las instrucciones del programa y subrutinas asociadas.
Debe aparecer una vez cada una en el programa fuente.

La segunda es el listado de instrucciones en el orden que se supone resolverá el problema.
37

La tercera son las subrutinas o funciones asociadas al programa, las cuales empiezan con
una etiqueta que las identifica y terminan con algunas de las variantes de RETURN.
SET DE INSTRUCCIONES
Todas las instrucciones pueden tener una etiqueta (Label) que las identifique.
Las líneas del programa no necesitan numerarse.
f identifica una dirección de RAM (registro).
k se usa para denotar un valor explícito (literal).
b se usa para identificar un bit en particular.
d se usa para direccionar el resultado de una operación. Por defecto, es 1, lo que indica que el
resultado se guardará en la dirección marcada por f (en el mismo registro que se usa para la
operación); en el caso de ser 0, el resultado de la operación se guardará en el registro de trabajo que denominaremos W.
38
ADDLW
Sintaxis
Operandos
Operación
Flags
Descripción
Sumar literal con w
Addlw k
0<=k<=255
(w)+k -> w
C; DC; Z
El contenido del registro w
se suma al literal k
ADDWF
Sintaxis
Operandos
Operación
Flags
Descripción
Sumar w con f
Addwf f,d
0<=f<=127 d= [0,1]
(w)+(f) -> destino (d)
C; DC; Z
El contenido del registrto
w se suma al contenido
de la dirección f
ANDLW
Multiplicación lógica entre l
yw
Andlw k
0<=k<=255
(w).and.k -> (w)
Z
El contenido del registro w
es “andeado” con el literal k
ANDWF
Multiplicación lógica
entre w y f
Andwf f,d
0<=f<=127 d= [0,1]
(w).and.(f) -> destino (d)
Z
El contenido del registro
w es andeado con el contenido de f
Sintaxis
Operandos
Operación
Flags
Descripción
BCF
Borrar un bit de la posición f
Sintaxis
Operandos
Operación
Flags
Descripción
Bcf f,b
0<=f<=127 ; 0<=b<=7
0 -> (f<b>)
BTFSS
Sintaxis
Operandos
Operación
Flags
Descripción
Prueba un bit, salta si es 1
Btfss f,b
0<=f<=127 ; 0<=b<=7
CALL
Sintaxis
Operandos
Operación
Flags
Descripción
Llamada a su rutina
Call k [label]
0<=k<=2047
(pc)+1 -> tos
K -> pc
Guarda el contenido actual
del PC y pone el contenido
Pone a 0 el bit b del contenido en f
Si el bit b del registro en f es
0, se ejecuta la siguiente
instrucción; si es 1, salta una
instrucción
Sintaxis
Operandos
Operación
Flags
Descripción
BSF
Sintaxis
Operandos
Operación
Flags
Descripción
Poner en 1 un bit de la
posición f
Bsf f,b
0<=f<=127 ; 0<=b<=7
1 -> (f<b>)
Pone a 1 el bit b del contenido en f
BTFSC
Sintaxis
Operandos
Operación
Flags
Descripción
Prueba un bit, salta si es 0
Btfsc f,b
0<=f<=127 ; 0<=b<=7
CLRF
Sintaxis
Operandos
Operación
Flags
Descripción
Borrar el contenido de f
Clrf f
0<=f<=127
00h -> (f)
Z
El contenido del registro
señalado por f pasa 00h
Si el bit b del registro en f
es 1, se ejecuta la siguiente instrucción; si es 0,
salta una instrucción
39
de k en el PC
40
CLRW
Borrar el contenido de w
COMF
Sintaxis
Operandos
Operación
Flags
Descripción
Clrw
Sintaxis
Operandos
Operación
Flags
Descripción
DECFSZ
Sintaxis
Operandos
Operación
Flags
Descripción
Decrementa f, salta si es 0
Decfsz f,d
0<=f<=127 d= [0,1]
(f)-1 -> (destino); salto si 0
INCF
Sintaxis
Operandos
Operación
Flags
Descripción
00h -> (w)
Z
El contenido del registro
señalado por w pasa 00h
Complementa el contenido de f
Comf f,d
0<=f<=127 d= [0,1]
(f)-1 -> destino
Z
Decrementa en 1 el contenido de f
GOTO
Sintaxis
Operandos
Operación
Flags
Descripción
Salto incondicional
Goto k [label]
0<=k<=2047
K -> pc
Incrementa contenido de f
Incf f,d
0<=f<=127 d= [0,1]
(f)+1 -> (destino)
Z
Se incrementa en 1 el contenido de f
INCFSZ
Sintaxis
Operandos
Operación
Flags
Descripción
Incrementa f, salta si es 0
Incfsz f,d
0<=f<=127 d= [0,1]
(f)+1 -> (destino)
IORLW
Sintaxis
Operandos
Operación
Flags
Descripción
Or inclusivo literal con w
Iorlw k
0<=k<=255
(w).or.k -> (w)
Z
El contenido del registro w
es “oreado” con el literal
IORWF
Sintaxis
Operandos
Operación
Flags
Descripción
Or inclusivo entre w y f
Iorlwf f,d
0<=f<=127 d= [0,1]
(w).or.(f) -> (destino)
Z
El contenido del registro
w se “orea” con el contenido de f
MOVF
Sintaxis
Operandos
Mueve el contenido de f
Movf f,d
0<=f<=127 d= [0,1]
MOVLW
Sintaxis
Operandos
Mueve un literal a w
Movlw k
0<=k<=255
El contenido del registro f
se decrementa en 1, si la
operación da 0 salta una
instrucción.
Salta a la dirección indicada por el literal.
El contenido del registro f
se incrementa en 1, si la
operación da 0 salta una
instrucción.
41
Operación
Flags
Descripción
(f)->(destino)
Z
El contenido del registro f
se mueve a otro destino
Operación
Flags
Descripción
k-> (w)
MOVWF
Mueve el contenido de w a
la dirección f
Movwf f
0<=f<=127
(w) -> (f)
NOP
No opera
Sintaxis
Operandos
Operación
Flags
Descripción
Nop
Sintaxis
Operandos
Operación
Flags
Descripción
RETFIE
Sintaxis
Operandos
Operación
Flags
Descripción
RLF
Sintaxis
Operandos
Operación
Mueve el dato del registro w
a la posición f
Retorna desde una interrupción
Retfie
Tos -> pc
1 -> gie
Retorna desde una interrupción, habilitando las interrupciones globales.
Girar a izquierda a través
del acarreo (carry)
Rlf f,d
0<=f<=127 d= [0,1]
cc
registro
RETLW
Sintaxis
Operandos
Operación
Flags
Descripción
RRF
Sintaxis
Operandos
Operación
El literal k se mueve al
registro w
Deja pasar un ciclo de
instrucción
Retorna desde una interrupción con un valor en w
Retlw k
0<=k<=255
Tos -> pc k -> (w)
Retorna desde una interrupción con un literal en
el registro w
Girar a derecha a través
del acarreo (carry)
Rrf f,d
0<=f<=127 d= [0,1]
cc
registro
Flags
Descripción
C
El contenido del registro f
es rotado un bit hacia la
izquierda usando el carry
Flags
Descripción
C
El contenido del registro f
es rotado un bit hacia la
derecha usando el carry
RETURN
Vuelve de una rutina
SUBLW
Sintaxis
Operandos
Operación
Flags
Descripción
Return
Sintaxis
Operandos
Operación
Flags
Descripción
Resta el literal del contenido de w
Sublw
0<=f<=127 d= [0,1]
k-(w) -> (w)
C, DC, Z
Resta el literal del contenido de w y pone el resul-
42
Tos -> pc
Retorna al programa principal desde una subrutina
tado en w
43
SUBWF
Sintaxis
Operandos
Operación
Flags
Descripción
XORWF
Sintaxis
Operandos
Operación
Flags
Descripción
Resta el contenido de w del
registro f
Subwf f,d
0<=f<=127 d= [0,1]
(f)-(w) -> (destino)
C, DC, Z
Resta el registro señalado
por w del contenido en f
XORLW
Sintaxis
Operandos
Operación
Flags
Descripción
O exclusivo entre w y el
literal
Xorlw k
0<=k<=255
(w).xor.k -> (w)
Z
El contenido de w se “xorea” con el literal y el
resultado va a w
O exclusivo entre w y f
Xorwf f,d
0<=f<=127 d= [0,1]
(w).xor.(f) -> (destino)
Z
El contenido de w se “xorea” con el contenido de f y
el resultado va al destino
Esta traducción puede (y de hecho arreglamos varios) tener errores. Conviene verificarlas antes
de ponerse de lleno a programar con ellas.
44
Ejemplo de un archivo de texto cuya denominación debe ser p16F628.inc para configurar el
ensamblado.
LIST
; P16F628.INC Standard Header File, Version 1.01
Inc.
NOLIST
Microchip Technology,
; This header file defines configurations, registers, and other useful bits of
; information for the PIC16F628 microcontroller. These names are taken to
match
; the data sheets as closely as possible.
; Note that the processor must be selected before this file is
; included. The processor may be selected the following ways:
;
;
;
;
;
1. Command line switch:
C:\ MPASM MYFILE.ASM /PIC16F628
2. LIST directive in the source file
LIST
P=PIC16F628
3. Processor Type entry in the MPASM full-screen interface
;==========================================================================
;
;
Revision History
;
;==========================================================================
;Rev:
;1.01
;1.00
Date:
Reason:
13 Sept 2001 Added _DATA_CP_ON and _DATA_CP_OFF
10 Feb 1999 Initial Release
;==========================================================================
;
;
Verify Processor
;
;==========================================================================
IFNDEF __16F628
MESSG "Processor-header file mismatch.
ENDIF
Verify selected processor."
;==========================================================================
;
;
Register Definitions
;
;==========================================================================
W
F
EQU
EQU
H'0000'
H'0001'
45
;----- Register Files-----------------------------------------------------INDF
TMR0
PCL
STATUS
FSR
PORTA
PORTB
PCLATH
INTCON
PIR1
TMR1L
TMR1H
T1CON
TMR2
T2CON
CCPR1L
CCPR1H
CCP1CON
RCSTA
TXREG
RCREG
CMCON
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
H'0000'
H'0001'
H'0002'
H'0003'
H'0004'
H'0005'
H'0006'
H'000A'
H'000B'
H'000C'
H'000E'
H'000F'
H'0010'
H'0011'
H'0012'
H'0015'
H'0016'
H'0017'
H'0018'
H'0019'
H'001A'
H'001F'
OPTION_REG
TRISA
TRISB
PIE1
PCON
PR2
TXSTA
SPBRG
EEDATA
EEADR
EECON1
EECON2
VRCON
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
H'0081'
H'0085'
H'0086'
H'008C'
H'008E'
H'0092'
H'0098'
H'0099'
H'009A'
H'009B'
H'009C'
H'009D'
H'009F'
;----- STATUS Bits -------------------------------------------------------IRP
RP1
RP0
NOT_TO
NOT_PD
Z
DC
C
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
H'0007'
H'0006'
H'0005'
H'0004'
H'0003'
H'0002'
H'0001'
H'0000'
;----- INTCON Bits -------------------------------------------------------GIE
46
EQU
H'0007'
PEIE
T0IE
INTE
RBIE
T0IF
INTF
RBIF
EQU
EQU
EQU
EQU
EQU
EQU
EQU
H'0006'
H'0005'
H'0004'
H'0003'
H'0002'
H'0001'
H'0000'
;----- PIR1 Bits ---------------------------------------------------------EEIF
CMIF
RCIF
TXIF
CCP1IF
TMR2IF
TMR1IF
EQU
EQU
EQU
EQU
EQU
EQU
EQU
H'0007'
H'0006'
H'0005'
H'0004'
H'0002'
H'0001'
H'0000'
;----- T1CON Bits --------------------------------------------------------T1CKPS1
T1CKPS0
T1OSCEN
NOT_T1SYNC
TMR1CS
TMR1ON
EQU
EQU
EQU
EQU
EQU
EQU
H'0005'
H'0004'
H'0003'
H'0002'
H'0001'
H'0000'
;----- T2CON Bits --------------------------------------------------------TOUTPS3
TOUTPS2
TOUTPS1
TOUTPS0
TMR2ON
T2CKPS1
T2CKPS0
EQU
EQU
EQU
EQU
EQU
EQU
EQU
H'0006'
H'0005'
H'0004'
H'0003'
H'0002'
H'0001'
H'0000'
;----- CCP1CON Bits ------------------------------------------------------CCP1X
CCP1Y
CCP1M3
CCP1M2
CCP1M1
CCP1M0
EQU
EQU
EQU
EQU
EQU
EQU
H'0005'
H'0004'
H'0003'
H'0002'
H'0001'
H'0000'
;----- RCSTA Bits --------------------------------------------------------SPEN
RX9
SREN
CREN
ADEN
FERR
OERR
RX9D
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
H'0007'
H'0006'
H'0005'
H'0004'
H'0003'
H'0002'
H'0001'
H'0000'
;----- CMCON Bits ---------------------------------------------------------
47
C2OUT
C1OUT
C2INV
C1INV
CIS
CM2
CM1
CM0
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
H'0007'
H'0006'
H'0005'
H'0004'
H'0003'
H'0002'
H'0001'
H'0000'
;----- OPTION Bits -------------------------------------------------------NOT_RBPU
INTEDG
T0CS
T0SE
PSA
PS2
PS1
PS0
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
H'0007'
H'0006'
H'0005'
H'0004'
H'0003'
H'0002'
H'0001'
H'0000'
;----- PIE1 Bits ---------------------------------------------------------EEIE
CMIE
RCIE
TXIE
CCP1IE
TMR2IE
TMR1IE
EQU
EQU
EQU
EQU
EQU
EQU
EQU
H'0007'
H'0006'
H'0005'
H'0004'
H'0002'
H'0001'
H'0000'
;----- PCON Bits ---------------------------------------------------------OSCF
NOT_POR
NOT_BO
NOT_BOR
NOT_BOD
EQU
EQU
EQU
EQU
EQU
H'0003'
H'0001'
H'0000'
H'0000'
H'0000'
;----- TXSTA Bits --------------------------------------------------------CSRC
TX9
TXEN
SYNC
BRGH
TRMT
TX9D
EQU
EQU
EQU
EQU
EQU
EQU
EQU
H'0007'
H'0006'
H'0005'
H'0004'
H'0002'
H'0001'
H'0000'
;----- EECON1 Bits -------------------------------------------------------WRERR
WREN
48
EQU
EQU
H'0003'
H'0002'
WR
RD
EQU
EQU
H'0001'
H'0000'
;----- VRCON Bits -------------------------------------------------------VREN
VROE
VRR
VR3
VR2
VR1
VR0
EQU
EQU
EQU
EQU
EQU
EQU
EQU
H'0007'
H'0006'
H'0005'
H'0003'
H'0002'
H'0001'
H'0000'
;==========================================================================
;
;
RAM Definition
;
;==========================================================================
__MAXRAM
__BADRAM
__BADRAM
__BADRAM
__BADRAM
H'01FF'
H'07'-H'09', H'0D', H'13'-H'14', H'1B'-H'1E'
H'87'-H'89', H'8D', H'8F'-H'91', H'93'-H'97', H'9E'
H'105', H'107'-H'109', H'10C'-H'11F', H'150'-H'16F'
H'185', H'187'-H'189', H'18C'-H'1EF'
;==========================================================================
;
;
Configuration Bits
;
;==========================================================================
_BODEN_ON
_BODEN_OFF
_CP_ALL
_CP_75
_CP_50
_CP_OFF
_DATA_CP_ON
_DATA_CP_OFF
_PWRTE_OFF
_PWRTE_ON
_WDT_ON
_WDT_OFF
_LVP_ON
_LVP_OFF
_MCLRE_ON
_MCLRE_OFF
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
H'3FFF'
H'3FBF'
H'03FF'
H'17FF'
H'2BFF'
H'3FFF'
H'3EFF'
H'3FFF'
H'3FFF'
H'3FF7'
H'3FFF'
H'3FFB'
H'3FFF'
H'3F7F'
H'3FFF'
H'3FDF'
49
_ER_OSC_CLKOUT
_ER_OSC_NOCLKOUT
_INTRC_OSC_CLKOUT
_INTRC_OSC_NOCLKOUT
_EXTCLK_OSC
_LP_OSC
_XT_OSC
_HS_OSC
LIST
50
EQU
EQU
EQU
EQU
EQU
EQU
EQU
EQU
H'3FFF'
H'3FFE'
H'3FFD'
H'3FFC'
H'3FEF'
H'3FEC'
H'3FED'
H'3FEE'
Tips & Tricks
Transcribo a continuación algunas subrutinas descriptas en las hojas de datos. Se supone que,
como están escritas por el fabricante del controlador, funcionan bien y son eficientes en el uso de
los recursos.
El PC (program counter) de este controlador es de 13 bits y, como debe poder guardarse en RAM
(de 8 bits), se compone de dos partes: los 8 bits de menor peso se denominan PCL y los 5 restantes PCLATH. En programas cortos, alcanza con guardar sólo el PCL y eso lo hace automáticamente con el llamado a una subrutina. Si el programa es largo y existe la posibilidad de que estemos ubicados más allá de la posición 128 de la memoria de programa (Flash en este caso), hay
que tomar el recaudo de guardar por nuestra cuenta los cinco bits adicionales. Esta subrutina tiene
en cuenta esta restricción.
Guarda registros importantes cuando atiende a una interrupción:
MOVWF
SWAPF
CLRF
MOVWF
MOVF
MOVWF
CLRF
…………..
………….
………….
MOVF
MOVWF
SWAPF
MOVWF
SWAPF
SWAPF
RETFIE
W_TEMP
STATUS, W
STATUS
STATUS_TEMP
PCLATH, W
PCLATH_TEMP
PCLATH
Copia W al registro TEMP
Transpone nibbles del reg. STATUS para guardar en W
Borra los bits de dirección de bancos. Se posiciona en B0
Copia el reg. STATUS en un registro STATUS_TEMP
Sólo se requiere si el programa usa más de un banco de m
Copia PCLATH en W
Banco de memoria 0
Instrucciones de la subrutina de atención a la interrupción
PCLATH_TEMP, W
PCLATH
STATUS_TEMP, W
STATUS
W_TEMP, F
W_TEMP, W
Copia PCLATH en W
Restaura el valor de PCLATH
Copia los bits del banco de m. usado en el progr. Ppal.
Se posiciona en el banco de memoria
Transpone nibbles del reg. W_TEMP
Reposiciona W al estado original
Retorna de la int. Activando las interrupciones globales
En esta subrutina deben haberse previsto registros usables de la RAM para ubicar los valores de
W , STATUS y PCLATH que aquí se denominaron W_TEMP , PCLATH_TEMP y STATUS_TEMP.
Subrutina de exploración para conocer la causa de una interrupción
BTFSC
CALL
INTCON, 1
INTE
BTFSC
CALL
BTFSC
CALL
INTCON, 2
TIMER
INTCON, 0
IPORTB
Si el flag de int. Externa es 1, brinca
INTE es una etiqueta que identifica la subrutina de atención a las interrupciones externas
Si el flag de la int. Por timer está activada, brinca
Timer es la etiqueta que identifica a subr. De atención
Flag que identifica una int. Por cambios en la puerta B
IPORTB es la etiqueta que identifica…….
51
Comprobar igualdad o identificar desigualdad
Se usan dos registros en memoria denominados REG1 y REG2 para comprobar su igualdad o
eventualmente cuál es mayor (o menor) que el otro.
MOVF
XORWF
BTFSC
CALL
MOVF
SUBWF
BTFSS
CALL
CALL
REG1, 0
REG2, 0
ESTADO, 2
IGUAL
REG1, 0
REG2, 0
ESTADO, 0
MAYOR
MENOR
REG1 se carga en W
REG1 xor REG2 y se carga en W
Si Z = 0 son diferentes y brinca
Si Z = 1 son iguales y llama a la subrutina IGUAL
REG1 se carga en W (puede tener cualquier valor)
Se opera como: REG1 – REG2 y se carga en W
Si C = 0 REG2 > REG1 y brinca
Si C = 1 REG2 < REG1 y llama a MAYOR
Viene de C = 0 REG2 > REG1 y llama a MENOR
Subrutina para pasar a diferentes bancos de memoria
Parto de la base de que están configuradas las variables STATUS (dirección del registro de estado), RP0 yRP1 (bits 5 y 6 del registro de estado). De acuerdo a las combinaciones de RP0 y RP1
estaremos trabajando en diferentes bancos de la RAM.
BANCO0
BANCO1
BANCO2
BANCO3
BCF
BCF
RETURN
BSF
BCF
RETURN
BCF
BSF
RETURN
BSF
BSF
RETURN
STATUS, RP0
STATUS, RP1
STATUS, RP0
STATUS, RP1
STATUS, RP0
STATUS, RP1
STATUS, RP0
STATUS, RP1
Pone a cero el bit 5 del reg. De estado
Pone a cero el bit 6 del reg. De estado
Retorna en el banco 0 de la RAM
Pone a uno el bit 5 del reg. De estado
Pone a cero el bit 6 del reg. De estado
Retorna en el banco 1 de la RAM
Pone a cero el bit 5 del reg. De estado
Pone a uno el bit 6 del reg. De estado
Retorna en el banco 2 de la RAM
Pone a uno el bit 5 del reg. De estado
Pone a uno el bit 6 del reg. De estado
Retorna en el banco 3 de la RAM
Teniendo escrita esta subrutina, cuando hay que cambiar de banco, se usará un llamado a alguna
de las entradas, por ejemplo CALL BANCO1.
Tablas
En el siguiente ejemplo se usa una tabla para que, dado un valor del 0 al 7 colocado en W, se obtenga el código binario para prender un indicador de “siete segmentos” y nos muestre el valor que
había en W. Los segmentos están conectados desde la a hasta la g en patas consecutivas del puerto B comenzando con RB0 y hasta RB6. El punto decimal (dp) está cableado a la pata RB7.
Hacemos una tabla en papel para ver las equivalencias entre los valores a visualizar y los segmentos que
deben prender en esa condición:
52
org 5
15
16
17
18 nada
19
20
21 display
22
23
24
25
26
27
28
29
30
31
movlw
call
movwf
goto
0x03
display
portb
nada
addwf
retlw
retlw
retlw
retlw
retlw
retlw
retlw
retlw
retlw
retlw
pcl
0x3F
0x06
0x5B
0x4F
0x66
0x6D
0x7D
0x07
0x7F
0x6F
0x00..al..0x09
Decimal
0
1
2
3
4
5
6
7
8
9
PortB
0011 1111
0000 0110
0101 1011
0100 1111
0110 0110
0110 1101
0111 1101
0000 0111
0111 1111
0110 1111
N°instr PC W
15
16 03
Wret
----
Hexa
3F
06
5B
4F
66
6D
7D
07
7F
6F
PortB
-----
La ventaja de este código es su extrema simpleza, la desventaja (y muy importante) es que las
tablas que podemos usar son limitadas porque ocupan posiciones de memoria destinadas al programa. Si se requieren tablas más largas hay que cablear una memoria ROM externa y usar direccionamiento indirecto, es decir que el código se complica bastante y la rapidez también se ve
afectada.
53
Programa para multiplicar dos binarios sin signo de 4 bits
#include<p16f628.inc>
cant
equ
0x20
F1
equ
0x21
F2
equ
0x22
aux
equ
0x23
org
5
clrf
aux
movlw 0x04
movwf cant
movlw 0x07
movwf f1
movlw 0x06
movwf f2
swapf f2,f
sigue
54
movfw cant
btfsc
status,z
goto
termina
bcf
status,c
rlf
aux,f
rlf
f2,f
btfsc
status,c
call
suma
decf
cant,f
goto
sigue
suma
movfw aux
addwf f1,w
movwf aux
return
termina
goto
termina
end
Programa para dividir un binario de 8 bits por uno de 4 bits, sin signo
#INCLUDE<P16F628.INC>
numerador
equ
0x20
divisor
equ
0x21
indice
equ
0x22
resultado
equ
0x23
org
5
sigue
divide
movlw
0x5a
movwf
numerador
movlw
0x05
movwf
divisor
clrf
resultado
clrf
indice
incf
indice
movfw
divisor
subwf
numerador,0
btfsc
status,c
goto
correr
bcf
status,c
rrf
divisor
55
bcf
status,c
rrf
indice
btfsc
status,c
goto
termina
bcf
status,c
movfw
divisor
subwf
numerador,1
btfss
correr
termina
s
tatus,c
goto
divide
movfw
indice
addwf
resultado,1
goto
divide
bcf
status,c
rlf
divisor
bcf
status,c
rlf
indice
goto
sigue
goto
termina
end
Referencias bibliográficas:
Microchip: PIC16F62X datasheet.
56
Apéndice A
Configuración del MPLab 8.3
Les preparé esta guía para que puedan configurar el MPLab como yo lo tengo configurado. No
quiere decir que sea la mejor ni la única, pero funciona.
Se comienza por generar un nuevo proyecto con el ayudante (Project Wizard)
Fig. 2
La primera pantalla es
como la Fig.2, simplemente una confirmación. En la siguiente
deberemos elegir el modelo de PIC, en nuestro caso el
16F628/872.
En la próxima seleccionamos la herramienta
para compilar, elegimos el MPASM (opción
por defecto)
En la siguiente seleccionamos un disco, carpeta
y nombre para guardar el proyecto.
La siguiente sirve para agregar archivos al proyecto. Agregamos el P16F628.INC para usarlo
como encabezado (#include…)
57
Finalización del wizard para generar el proyecto. Aceptamos para volver a la pantalla del
MPLab.
.
Por
defect
o,
habrá
dos ventanas:
a) el contenido actual del proyecto donde
debe aparecer entre las carpetas la de
encabezado con el archivo INC elegido
antes y
b) la ventana Salida (output) que aparece
vacía.
Ir al menú Project, Build options y elegir la solapa para
configurar el MPASM Assembler.
Tildar la opción para deshabilitar la distinción entre mayúsculas y minúsculas, de forma que si editamos el programa
con minúsculas, el ensamblador reconozca el archivo de
encabezado que habitualmente está escrito en mayúsculas.
Verificar la opción de que trabaje en hexadecimal por defecto.
Lo siguiente es elegir la herramienta para depuración (debugger), tildamos MPLabSim.
Después de esta elección debería aparecer una barra de herramientas que nos permitirá compilar
y correr la compilación, ejecutar instrucción por instrucción, colocar puntos de detención, etc.; es
decir serán las herramientas de depuración de nuestro programa (subrutina, función).
Lo que deberemos elegir a continuación es la frecuencia del reloj para la simulación: menú Debugger (depuración), Settings (configuración), solapa
Osc/Trace: elegir la frecuencia en el valor que usaremos comúnmente: 4 MHz. Se podría elegir la otra
58
opción de frecuencia que es 37 KHz, dependiendo de nuestro diseño.
Ahora sigue configurar al
PIC, a través de la “palabra
de configuración”. Esta palabra puede estar definida en el
programa o aquí.
Esta palabra establece los
siguientes parámetros para el
funcionamiento del PIC:
a) El tipo de reloj (oscilador) que va a usar
(interno, externo, por cristal, etc.) Nosotros usaremos el RC interno porque no necesitamos una precisión extrema y no inutilizamos dos de las patas de E/S del PIC.
b) El timer denominado ”perro guardián” lo usaremos deshabilitado
c) El timer de encendido también deshabilitado
d) Habilitaremos el borrado total en un reset.
e) No interesa el siguiente que es la detección de apagado
f) La siguiente opción debe habilitarse para poder luego programar el PIC con tensiones
normales (de lo contrario hace falta mayor tensión para programarlo)
g) Deshabilitamos la protección de lectura de la EEPROM
h) Deshabilitamos la protección del código del programa
Lo que sigue es quizás un poco engorroso, pero no se me ocurrió hasta ahora una mejor forma
para hacerlo.
Conceptualmente se trata de agregarle el archivo de texto donde estará definido nuestro programa; el problema es que el editor de textos es accesible sólo desde la aplicación y no he encontrado la manera de que agregue este archivo a la carpeta “archivos fuente” (source files) del proyecto y debe estar allí antes de ordenar la compilación porque el compilador busca en esa ruta el archivo de texto cuya extensión es .ASM.
De modo que a seguir los pasos:
1) Llamar al editor
desde el menú Archivos (File), nuevo (New). Aparecerá una ventana
con el nombre “untitled” (sin título).
2) Hay que darle
uno o dos
TABs y pondremos cualquier cosa, por
ejemplo la directiva para que use el encabezado que incluimos antes: #include<p16f628.inc>
59
3) Cerrar el editor, guardándolo con un nombre (mejor el del proyecto) y la extensión
.ASM. (muy importante)
4) Cerrar el proyecto confirmando que se quiere guardar todo el espacio de trabajo, para que
cuando lo abramos aparezcan las ventanas que están ahora.
5) Abrir el proyecto, ir a
la carpeta “source files”, apretar botón derecho del mouse y seleccionar “add files”
(agregar archivo), buscar el archivo recién
guardado y abrirlo.
Deberá aparecer dentro
de la carpeta seleccionada.
6) Hacer doble click en el archivo para abrirlo.
Fin del proceso de agregar archivo fuente.
Se abrirá el editor junto con el archivo
guardado, deberíamos ver que ahora
tiene los números de línea y ha cambiado el color del texto.
Estos colores indicarán si el precompilador “entiende” la sintaxis de
la instrucción que estamos poniendo.
Hay que tener en cuenta que la primer
columna de la izquierda está reservada para etiquetas (labels), la segunda
columna es para las instrucciones, la
tercera es para los argumentos que
necesitan las instrucciones y, eventualmente, la cuarta es para los comentarios; éstos deben estar separados por el carácter “;”.
Los comandos del pre-procesador (include, radix, org, equ, list, start/end) no deben estar en la
primera columna.
60
Escribir el programa como en un editor de texto común usando TAB o SPACE para espaciar y
ENTER para cambiar de línea.
Agregar END al final para avisarle al pre-procesador que ahí termina la compilación.
COMPILACIÓN:
Finalizada la edición del programa hay que compilarlo haciendo click aquí
Aparecerá una ventana de diálogo preguntando si quieren direcciones relativas o absolutas, responder absolutas. En la ventana “salida” (output) se verá el progreso de la compilación, figurando
en primer lugar las advertencias (warning) y los errores. Hay que corregir los errores de sintaxis
hasta que sólo aparezcan advertencias y la leyenda de “compilación exitosa” (building succeeded). Las advertencias son generalmente porque faltaron argumentos en las instrucciones y el
compilador tomó los valores que tiene por defecto o porque hay registros que están ubicados en
bancos de memoria diferentes al cero y les avisa que ese registro está en el banco xx.
Si el programa funciona, quiere decir que las advertencias no tienen importancia.
Si el programa no funciona, comenzar a depurarlo verificando las advertencias que hizo el compilador. Si cambian algo del programa hay que compilarlo nuevamente.
Para correr el programa hay que hacer click acá
Si el programa no finaliza nunca, para pararlo hacer click acá
Para resetear el programa se usa este botón (debe estar previamente parado)
Para correr instrucción por instrucción
Para agregar puntos de parada
61
Depurar un programa sin ver el valor de los registros sería imposible, es por eso que el MPLab
tiene una cantidad de herramientas para poder echar un vistazo a lo que ocurre dentro del PIC a
medida que transcurre el programa. Veremos sólo las que podrían usar más:
a) Menú Ver, opción Watch:
b) Menú Ver, opción Hardware stack
c) Menú Ver, opción EEPROM
d) Menú Debugger, opción StopWatch
Watch:
Abierta la ventana, conviene configurarla haciendo
click con el botón derecho del mouse, elegir
Properties, solapa Preferences y tildar la opción
“Expand SFR registers”, esto permite ver cada
uno de
los bits (flags) que componen los registros del
PIC;
por ejemplo el registro de estado de la ALU
tiene
los flags que indican un desbordamiento (C),
si la
operación dio cero (Z) o el banco de memoria
en
uso. Para no tener que decodificar el estado de
cada
uno de ellos desde el valor hexadecimal del
registro, con esta opción se despliegan dentro del
registro que queramos cada uno de esos flags y el
valor
(0 o 1).
Una vez compilado, en la opción “add symbol”
aparecen las variables que hayamos usado en
RAM
para el diseño de nuestro programa. La opción
“Add
SFR” contiene los registros especiales como el de estado, el de trabajo y los puertos, entre muchos otros.
Esta es la ventana indispensable para la depuración.
Hardware stack
Si tenemos problemas de lógica, habiendo usado varias llamadas a subrutinas, puede ser que hayamos olvidado algún RETURN y se está llenando la “pila”. En esta ventana podemos ver cada
una de las llamadas y si regresa o no de cada una de ellas.
EEPROM
Nos muestra el contenido de la memoria permanente de lectura/escritura y permite, además, escribir en ella. Los valores que escribamos aquí serán puestos en el PIC cuando grabemos el programa en él.
StopWatch
Esta ventana nos muestra cuántos ciclos de instrucción (o tiempo) tardó nuestro programa hasta
que paró, sea esto porque terminó o por una parada programada por nosotros con el símbolo “B”
rojo que vimos antes.
62
Existen otras herramientas de software para simular el uso de los PIC, algunas más visuales, pero
como esta es gratuita y está hecha por el fabricante del PIC, me merece más confianza que las
otras. Ahora resta que usen este software para acostumbrarse a las posibilidades que tiene y que
practiquen porque es una de las bases de la programación.
63
Descargar