Hola de nuevo.
Durante la última semana iniciamos las especificaciones de nuestro
procesador sencillo utilizando para ello dos ejemplos de sistemas digitales,
el controlador de temperatura y el cronómetro.
Esta semana daremos una especificación más formal y más precisa de dicho procesador.
Y para ello empezaremos por una especificación funcional de
nuestro procesador.
Veamos en primer lugar los tipos de instrucción.
Se han definido ocho tipos de instrucción u ocho instrucciones distintas
y para cada instrucción se ha definido un código que la identifica y la lista de
parámetros necesarios para la ejecución e interpretación de dicha instrucción.
El código de instrucción será una palabra o un par de palabras que
identifican a cada instrucción con lo que esa instrucción hace o ejecuta.
Así por ejemplo la primera instrucción de la lista es la "assign_value, k, A"
assign_value es el código, k y A son los parámetros y lo que hace esta instrucción
es asignar el valor de la constante A al elemento de memoria X_k.
La siguiente es "data_input, k, j"
y lo que hace es que el valor que entra por el puerto j,
puerto de entrada j se asigna al elemento de memoria k.
La siguiente es "data_output, i, j" y lo que hacemos en este caso es el
contenido de un elemento de memoria lo llevamos hacia el puerto de salida
en este caso el elemento j de memoria al puerto i de salida.
La instrucción "output_value, i, A" hace lo mismo pero ahora
lo que enviamos al puerto i de salida es una constante A. y en la siguiente
que es "operation i, j, k, f" significa que se debe realizar una operación,
la operación es calcular la función f cuyos,
cuyos parámetros o cuyas entradas son X_i,
X_j y el resultado lo dejaremos sobre X_k.
Este es el significado de esta instrucción "operación, i, j, k, f".
En este caso f es un identificador de la operación por ejemplo, una suma,
una diferencia, una multiplicación, entre otras.
De hecho es una operación que algunos de los recursos de cálculo de nuestro
procesador puede ejecutar tal como explicaremos más adelante.
La instrucción siguiente es el "jump N", significa que la instrucción
siguiente a evaluar o a ejecutar es la que está en la posición N.
Por lo tanto es como un "go_to N" de
las descripciones funcionales que habíamos hecho hasta ahora.
La siguiente el "jump POS, i, N" es un salto,
el anterior era un salto incondicional, este es un salto condicional y
la condición es si el elemento de memoria X_i es positivo,
es mayor que cero entonces saltamos a la instrucción, vamos a ejecutar como la
instrucción siguiente la que está en la posición N de memoria.
Y en caso contrario seguimos en secuencia.
Y la otra instrucción de salto condicional es el "JUMP NEG",
si el valor de la posición de memoria o del elemento de memoria X_i es negativo,
es menor que cero entonces saltamos a la instrucción que está en la posición N.
En caso contrario seguimos en secuencia.
Y con estas instrucciones intentaremos hacer programas.
A modo de ejemplo con estas nuevas instrucciones o tipos de instrucción,
este algoritmo de control de temperatura que teníamos aquí,
que habíamos visto, descrito de esta manera lo podemos reescribir
de esta otra que tenemos aquí utilizando las instrucciones que acabamos de ver.
Así por ejemplo, en lugar de X5 igual a 10 que significa poner
en la posición de memoria X5, en el elemento de memoria X5 la constante 10.
lo que haríamos es utilizar la instrucción "assign_value, 5, 10", ¿de acuerdo?
O por ejemplo en la instrucción condicional que
tenemos aquí "if X4 menor que 0 go to 7",
la reescribiríamos ahora con nuestro nuevo conjunto de
instrucciones como un salto si negativo, si X4 menor que 0, si negativo,
si X4 es menor que 0 saltamos a la posición 7
y la posición 7 sería saltar a esta instrucción que tenemos aquí.
Si no seguiríamos en secuencia por la 5.
Y así sucesivamente podríamos ir trasladando todas estas
operaciones o instrucciones funcionales a traducirlas
a instrucciones de este conjunto que acabamos de definir.
El sistema digital que queremos implementar tiene ocho puertos
de entrada de datos, el IN0, IN1 hasta IN7, cada uno de ellos de ocho bits,
tiene ocho salidas u ocho puertos de salidas de datos,
desde el OUT0 hasta el OUT7, también de ocho bits, tiene una entrada instruction,
esta que tenemos aquí, cuyo valor corresponde a uno de los
ocho tipos de instrucciones que acabamos de ver y sus correspondientes parámetros,
es decir, tanto el nombre de la instrucción, el código de la
instrucción como los parámetros necesarios entran por esta señal de instruction.
Y tiene una salida número o number que selecciona la siguiente
instrucción a ejecutar de este programa que tenemos aquí abajo.
Un comentario importante.
Hasta ahora hemos asumido que nuestro procesador recibía instrucciones
desde un dispositivo externo que contenía el programa y que dicho dispositivo
externo recibía información en forma de condiciones desde nuestro procesador.
Estas condiciones eran indicaciones como por ejemplo X_i es menor
que 0 o un determinado elemento de memoria X_k es positivo,
mayor que 0. Condiciones de este estilo y similares.
Entonces el dispositivo externo o
programador tomaba decisiones sobre el siguiente número de
instruccione a ejecutar en función de este tipo de condiciones.
En la nueva especificación de nuestro procesador se asume que el
número de la instrucción siguiente a ejecutar se
computa internamente en el propio procesador, es decir, aquí dentro.
Luego el procesador recibirá instrucciones, ejecutará,
aquí dentro ejecutará dichas instrucciones, y calculará el número de la instrucción
siguiente a ejecutar y es el que sacará por esta salida que tenemos aquí.
Con esta nueva especificación, el conjunto de instrucciones que
constituyen un programa está almacenado en algún tipo de memoria.
Y este tipo de arquitecturas capaces de ejecutar
programas almacenados en memoria se llaman las arquitecturas von Neumann.
A continuación veremos una especificación funcional de
nuestro procesador en forma de algoritmo.
En primer lugar, ponemos el valor de number a 0, lo que significa es que la
primera instrucción que se va a ejecutar será la instrucción número 0, es decir
la que está en la posición 0 de la memoria donde almacenamos nuestro programa.
Después de esto, el procesador ejecuta un bucle infinito
indicado por este loop que tenemos aquí,
que realiza las acciones siguientes:
lee la instrucción indicada por nuestro número, el number,
de nuestro programa leemos la instrucción indicada por number en este caso la
primera vez sería la instrucción que está en la posición 0 y esa instrucción es la
que entra por la señal de instrucción a nuestro procesador.
Y entonces el procesador determina de qué instrucción se trata
de forma que si la instrucción es una instrucción de tipo "assign_value, k, A",
lo que hace es que sobre el elemento de memoria X_k pone el valor
de la constante A, la constante A venía especificada en la propia instrucción.
Y a continuación incrementa en uno el valor de "number" de
forma que apuntamos a la instrucción siguiente en secuencia para ser ejecutada.
En el caso de que esa instrucción que hemos leído de nuestro programa aquí
arriba sea una instrucción de tipo "data_input, k, j",
lo que hacemos es que el valor del puerto de
entrada IN_j se instala en el elemento de memoria X_k.
Y de nuevo incrementamos en uno el "number".
Si esa instrucción era un "data_output, i, j", lo que se hace sobre
el puerto de salida i out_i ponemos el valor del elemento
de memoria X_j, y una vez más incrementamos en uno nuestro "number".
Cuando la instrucción leida o en curso de ejecución es "output_value, i, A",
es el valor de la constante A que viene en la propia instrucción quien se
transmite al puerto de salida out_i, y de nuevo incrementamos
en uno nuestro "number" para que apunte a la instrucción siguiente en secuencia.
Cuando se trata de una instrucción "operation, i, j, k, f", lo que debemos,
lo que debe hacer el procesador es realizar esa operación indicada
por f utilizando como operandos el valor contenido en la memoria
X_i y el valor contenido en la memoria X_j y el resultado de esa
operación f lo depositamos en el elemento de memoria X_k,
y una vez más incrementamos en uno el number.
Y ahora vendrían las tres instrucciones de salto condicional o incondicional
que son las que permiten romper la ejecución secuencial de un programa.
Tenemos el "jump N", si se trata de una instrucción "jump N"
es una instrucción de salto incondicional y significa que hay que
ir a ejecutar la instrucción que está en la posición N de memoria.
Luego lo único que hay que hacer para ejecutar esta instrucción es poner en
number no el number más uno que poníamos habitualmente en
las anteriores sino este valor de N que viene en la propia instrucción,
de forma que la siguiente instrucción a ejecutar será la de la posición N.
Si en cambio se trata de un salto condicional a que un elemento de memoria,
el elemento X_i sea positivo, lo que estamos haciendo en esta instrucción
es si el valor contenido en el elemento de memoria X_i es mayor que 0,
es positivo, entonces hay que ir a ejecutar la instrucción que está
en la posición N que venía indicada en la propia instrucción que estamos ejecutando.
Luego en ese caso si se cumple la condición lo que hay que hacer es
poner N sobre el valor de number.
Si no, si no se cumple esta condición, sencillamente seguimos en secuencia,
por lo tanto incrementamos number en una unidad.
Y lo mismo para la siguiente que es un salto condicional si negativo,
si la posición de memoria X_i tiene un valor negativo menor que cero,
entonces saltamos a la instrucción N y si no seguimos en secuencia.
Y con esto hemos visto un modelo funcional en forma de algoritmo muy simple,
este es el algoritmo de nuestro procesador.
Pasemos ahora a realizar un ejercicio.
Para ello partimos de los supuestos siguientes:
que el conjunto de elementos de memoria X está formado por 16 elementos
o 16 componentes y cada uno de ellos puede almacenar números
de ocho bits, estos son los X0 a X15.
Que hay ocho puertos de entrada y ocho puertos
de salida y también todos y cada uno de ellos de ocho bits.
Que el número máximo de instrucciones de un
programa será de 256 y que la operación f solo hay dos
posibilidades en cuanto a operaciones que son: la suma y la resta.
Bien, pues en base a estos supuestos, la pregunta es,
¿cuántas instrucciones distintas se pueden llegar a definir?
Veamos cual es la solución.
Para cada tipo de instrucción posible calculamos el número de
valores que cada uno de sus parámetros puede tomar.
Luego calculamos cuantas combinaciones posibles hay de
todos esos parámetros y sus valores, y ese resultado es el número
de instrucciones distintas para cada tipo de instrucción disponible.
Veamos un ejemplo.
En el caso de el primer tipo de instrucción, el "assign value, k, A"
k es el número de elementos de memoria, que ya hemos fijado en 16.
16.
A es un valor constante de ocho bits,
luego el número máximo de valores distintos es dos a la ocho, o sea 256.
Por lo tanto, el número total de posibles instrucciones de ese tipo,
del assign, es el resultado de multiplicar el número de elementos de memoria 16
por el número máximo de posibles valores de A que eran
256 y esto nos da un total de 4096 posibles instrucciones.
Si repetimos estos mismos cálculos para todos los tipos de instrucción
considerados y sumamos estos valores resultantes, lo que obtendremos
al final es el valor de 23040 instrucciones distintas.
Una observación interesante que podemos hacer ya en este momento,
es que el número total de instrucciones, estas 23040,
está comprendido entre dos a la 14 y dos a la 15.
Luego más adelante cuando necesitemos codificar todas estas
instrucciones distintas en códigos binarios,
ya sabremos de antemano que el número mínimo de bits
que necesitamos para ello es 15, este 15 que tenemos aquí.
Esto ya nos da una pista de cual será precisamente el
número necesario de bits para codificar nuestras instrucciones.
Y como resumen de esta lección diremos que hemos definido un conjunto de
instrucciones que nuestro procesador sencillo es capaz de ejecutar.
Se ha introducido el concepto de arquitectura von Neumann,
arquitectura basada en la idea de ejecutar programas almacenados en
memoria y se ha propuesto una especificación funcional en
forma de algoritmo sencillo para nuestro procesador.
Y con esto finalizamos la sesión.
Gracias y hasta la próxima.