[MUSIC] Vamos a continuar con la descripción de nuestro procesador, utilizando los dos ejemplos de la primera lección. El primer ejemplo era un controlador de temperatura que se puede describir por medio de este algoritmo. Obsérvese que el algoritmo se ha simplificado ligeramente. Hemos asumido que la precisión en temperatura es de un grado y no de medio grado como en descripciones anteriores. Si la temperatura medida en TMP, en TEMP, es menor que la posición marcada en el selector externo. Entonces tenemos que activar la señal de salida On-Off a on. Pero si la temperatura es mayor que la posición seleccionada en el selector externo entones esa señal de salida la tenemos que poner a off. En caso de que la temperatura actual, sea igual a la posición seleccionada no hay que hacer absolutamente nada sobre on-off, on-off no cambia si estaba on sigue on, si estaba en off sigue en off. Para implementar este algoritmo utilizaremos nuestro sistema genérico. Es decir, nuestro procesador. Y para ello lo que haremos es asignar la temperatura medida en el exterior la conectaremos o entrará por el puerto In 0. La posición, la temperatura por In0. La posición de temperatura seleccionada en el exterior entrará por In 1. La salida On-Off que controla la caldera saldrá por el OUT 1. Y habrá un componente externo, que será nuestra base de tiempos, que será como un reloj externo que conectaremos, que nos dará nuestra base de tiempos y que conectaremos a la entrada In 2. Esta base de tiempo es la que utilizaremos para calcular si han transcurrido los diez segundos de tiempo de espera que requiere este wait. Asumimos que nuestro procesador es capaz de ejecutar este conjunto de instrucciones que tenemos aquí y que you habíamos visto el otro día: enviar una constante sobre un elemento de memoria interno. Enviar la información que viene por una entrada externa a un elemento de memoria interno. O sacar hacia puertos que van hacia el exterior, you sea el contenido de un elemento de memoria interno o una constante. Estas serían operaciones de entrada y operaciones de salida de información. También es necesario ser capaces de que nuestro sistema sea capaz de interpretar una instrucción del tipo que tenemos aquí. De sumar el contenido de una posición o de un elemento de memoria Xj con el contenido de otro elemento de memoria Xk. Y dejar el resultado en Xi. Esta sería la operación de suma y lo mismo con la operación de resta. Finalmente, nos quedan las instrucciones de salto condicional o incondicional y entre ellas tendríamos la de salto incondicional, es decir, cuando nos encontramos esta instrucción go to n. Significa vamos a ejecutar la instrucción que está en la posición de memoria o que lleva la etiqueta n, de forma incondicional. Y estas dos que son instrucciones de bifurcación condicional. Si se cumple la condición, en este caso la condición es si el contenido de Xi es menor que 0, es decir, si es negativo. Entonces, saltamos a ejecutar la instrucción que está en la posición n o, en este caso, si el contenido de Xi es mayor que 0 vamos a ejecutar la instrucción n. En este caso sería equivalente a preguntar si ese valor de Xi es positivo. Hay otras características, como puede ser el número de bits de estos elementos o de estos puertos de entrada o de salida. El número, cuántos puertos de entrada y cuántos puertos de salida tenemos. O cuántos elementos de memoria internos tenemos que los iremos definiendo más adelante. Para poder realizar el algoritmo de control de temperatura se necesitan algunos elementos de control de memoria interna que almacenarán los correspondientes variables del algoritmo. En X0, almacenaremos el valor de la temperatura actual medida temp, X0, leída desde el puerto de entrada IN0. En X1 leeremos o guardaremos la información sobre el selector de temperaturas, la posición de dicho selector que hemos leído por el puerto in1. Aquí tenemos los puertos de entrada. En x2 guardaremos el valor del tiempo que viene de ese reloj externo y que hemos leido por in2. En x3 se guardara el valor del tiempo al inicio de nuestra instrucción, wait for diez segundos. En X4, el elemento de memoria X4 servirá para guardara resultados de cálculos internos. Y en el X5 guardaremos el valor de la constante el valor diez. En relación con las operaciones hemos visto que solo necesitaremos la suma y la resta. De hecho, esta ultima la utilizaremos, la resta la utilizaremos solo para poder evaluar condiciones del tipo, ¿es el contenido de una posición de memoria positivo o negativo?. Y de esa forma poder enviar la condición correspondiente por la salida de condición. En base a todos estos elementos y el conjunto de instrucciones definido, vamos a empezar a describir el programa que este sistema genérico, el procesador, tendrá que ejecutar para realizar el algoritmo en cuestión. Aquí tenemos el algoritmo y en primer lugar, trataremos de representar este algoritmo en forma de diagrama de flujo que es lo que tenemos de este lado de la transparencia. De forma que el diagrama de flujo será la primera aproximación al programa que tendrá que ejecutar nuestro procesador. En este diagrama de flujo, iremos reflejando todas y cada una de las instrucciones de nuestro algoritmo. Más algunas otras necesarias para poder materializar ese algoritmo en nuestro sistema genérico. Por ejemplo, empezamos por una instrucción que es poner el valor constante 10 en X5 que es lo que habíamos indicado aquí. A continuación, leemos el valor de la temperatura externa en X0, tal como habíamos también dicho que asociaríamos ese valor a la, o guardaríamos ese valor en la posición de memoria X0. A continuación hacemos lo mismo con el valor pos, el valor que viene del selector de temperaturas y lo guardamos en X1 tal como habíamos indicado. Ahora hacemos una diferencia, una operación de resta. Al X0 le restamos X1, es decir, a la temperatura medida le restamos la temperatura seleccionada y esto para qué lo hacemos pues básicamente para poder hacer esta comparación que tenemos aquí. A continuación en concreto esa comparación la hacemos aquí en esta sentencia de nuestro diagrama de flujo, y ahí preguntamos si X4 es menor que 0. Si X4 es menor que 0, es decir, es negativo, esto significa que aquí X1 es decir, pos, la temperatura seleccionada era mayor que la temperatura actual. Si la temperatura seleccionada era mayor que la actual, tenemos que activar nuestra caldera para aumentar la temperatura ambiente. Y esto es lo que hacemos en esta parte de nuestro diagrama de flujo. En cambio si la temperatura si X4 no es negativo tenemos que mirar si X4 es positivo lo que significaría que X0, la temperatura actual, es mayor que la temperatura solicitada, que X1, y en este caso si es mayor lo que tenemos que hacer es detener la calefacción. Y es lo que hacemos aquí. En el caso de que no sea ni mayor ni menor significaría que es igual y, en ese caso, aquí you abríamos acabado con toda esta parte de aquí. Lo que hemos hecho es finalizar con esta instrucción de aquí. Está instrucción se refleja en este diagrama de flujo en este trozo de nuestro diagrama de flujo. Ahora vendría la instrucción siguiente wait for 10 segundos. Esta instrucción de wait for 10 segundos la materializaremos o la reflejaremos en este diagrama de flujo, en esta parte de aquí. Para ello empezamos leyendo sobre nuestra posición de memoria o nuestro momento de memoria x3 tal como habíamos dicho aquí. El valor al inicio de la ejecución de esta instrucción de wait for 10 nanosegundos. Luego leemos de nuevo es el valor del tiempo, habrá transcurrido un tiempo de ejecución de una determinada instrucción como esta de aquí y a continuación calculamos la diferencia entre esos dos tiempos. Es decir el tiempo que ha transcurrido entre una y otra lectura. Entre el tiempo inicial y la última lectura que hemos hecho del tiempo. Esa diferencia que es el tiempo transcurrido lo restamos de el valor constante 10. 10 es el número de segundos que queremos calcular y por lo tanto, esta diferencia nos da el número de segundos que todavía tienen que transcurrir para que hayan transcurrido estos 10 segundos. Y esa es la pregunta que hacemos aquí: si X4 es negativo, X4 es la diferencia entre el tiempo transcurrido y el tiempo de espera previsto. En el caso de que sea negativo, significa que todavía no han transcurrido esos 10 segundos y si no han transcurrido esos 10 segundos, nos volvemos a buclear aquí para seguir dejando transcurrir un tiempo, cuando se hayan cumplido esos 10 segundos, habremos revisado toda esta instrucción y fijaos que aquí tenemos un bucle, pues aquí tenemos que volver a empezar el bucle. No hace falta volver a la primera instrucción porque en X5 pusimos un valor constante 10 y X5 no lo hemos modificado. Esta sería la forma de convertir un algoritmo en un diagrama de flujo, que es lo que tenemos aquí. Ahora para obtener el programa correspondiente a este diagrama de flujo como una secuencia de instrucciones, sólo necesitamos enumerar todas las instrucciones de nuestro diagrama de flujo, etiquetándolas con su número correspondiente en el diagrama de flujo y una vez estén todas enumeradas en nuestro diagrama de flujo, ordenarlas por dichos números de instrucción o etiquetas de instrucción tal como se muestra aquí. Y aquí tenemos nuestro diagrama de flujo y aquí tenemos el programa correspondiente. Vamos a ver cómo hacemos esta enumeración. Aquí vemos las tres o cuatro primeras instrucciones, sencillamente las enumeramos secuencialmente: primero hay que hacer esta, después hay que hacer esta y así sucesivamente, luego esto es la 0, la 1, la 2, la 3 y la 4. Cuando llegamos a la 4, esta es una instrucción de salto condicionado. Si no se cumple la condición seguiríamos por la instrucción siguiente y esta sería la 5 que de nuevo es un salto condicional y si no se cumple seguiríamos y ahora vemos que tenemos que ir a una instrucción que todavía no hemos enumerado. En principio si no se cumple lo que tendríamos aquí es una instrucción 6 que sería un salto incondicional a esta instrucción de aquí. Bien you tenemos enumeradas hasta la 6 veamos que pasan las ramas de estas instrucciones condicionales que no hemos evaluado. La primera si se cumple la condición teníamos que ir a activar la caldera. Poner On-Off a 1, pues esta será la instrucción 7 porque habíamos llegado hasta la 6 y está será la 7 y después de la 7 de nuevo debemos saltar a esta instrucción. Que todavía no hemos enumerado, luego habría una instrucción 8 que de nuevo sería un GO TO a esta instrucción. La otra sentencia condicional que era esta de aquí y evaluamos qué pasa si se cumple la condición. Si se cumple la condición tenemos que parar la caldera, tenemos que poner On- Off a 0. Esto es la instrucción 9 y después de la instrucción 9 you seguimos en secuencia y vamos a la siguiente. La siguiente sería esta y esta la enumeramos ahora como 10. Luego ahora you sabemos que este GO TO de la instrucción 8 y este GO TO de la instrucción 6 son GO TO a la instrucción 10. Podemos ver cómo estas condiciones, esta parte de aquí que es un poco más compleja, cómo se refleja en nuestro programa. La instrucción 4 es esta que tenemos aquí. Sí X4 es menor que 0 entonces si X4 si se cumple la condición esto es un go to a la instrucción 7. La instrucción 7 lo que hace es poner el OUT 0 lo pone a 1 sabiendo que este estaba conectado a la señal On-Off. Si esta condición, si la condición de aquí no se cumple salimos por este lado. Y ejecutamos la siguiente en secuencia, vemos que la siguiente en secuencia es la 5, que es esta pregunta de aquí, que de nuevo sí se cumple si x4 es mayor que 0, hay que hacer un go to a 9. 9 es esta sentencia de aquí, que es la que nos ponía long off a 0 poniendo 0 en el puerto OUT 0. Si está condición no se cumple continuamos en secuencia a la 6 y la 6 era un GO TO a la instrucción 6 que era esta que teníamos aquí. Bien sigamos con el resto del el diagrama de flujo, enumeramos las tres siguientes como 11, 12 y 13 de nuevo tenemos una 14 que es condicional. Si la condición se cumple habría un GO TO a la 11. Y si no se cumple también en este caso la instrucción siguiente de esta condición, la 15, sería un GO TO a volver a empezar, un GO TO a la sentencia 1 y esto es lo que vemos aquí. ¿De acuerdo? Esta condición de aquí abajo está reflejada aquí. Pues bien ahora que hemos etiquetado todo nuestro diagrama de flujo, lo único que hay que hacer para obtener el programa correspondiente que es este que tenemos aquí. Es listar todas estas instrucciones del diagrama de flujo en el orden indicado por las etiquetas que les hemos asignado. El segundo ejemplo es el cronómetro definido por este algoritmo. Asumiremos que nuestro sistema genera el valor de tiempo de salida bajo la forma de décimas de segundo. Y que otro circuito conectado a la salida de este algoritmo, un circuito que estaría aquí, convierte las décimas de segundo en números de horas, minutos, segundos y décimas de segundo que envía al display correspondiente. Luego la operación de actualizar el tiempo de nuestro algoritmo es una simple suma para incrementar el tiempo actual en décimas de segundo. En relación con los puertos de entrada-salida los conectaremos de la siguiente forma: el interruptor RESET que tenemos aquí, lo conectaremos al puerto IN0. El interrumpor START a puerto IN1, el STOP a puerto IN2 y la señal periódica de referencia temporal externa REF la conectamos al puerto IN3. Por último, el valor calculado de tiempo por cronómetro se conectará a través de un puerto OUT0 a la señal externa TIME. En este segundo ejemplo solo utilizaremos cuatro elementos de memoria internos. En X0 servirá para almacenar los valores de RESET, START o STOP leídos a través de los puertos IN0, IN1 e IN2. X1 contendrá el valor de la referencia temporal externa REF leída a través del puerto IN3 y que utilizaremos para ir incrementando el tiempo transcurrido o contabilizado por el cronómetro. X2 contiene el tiempo calculado por el cronómetro, por lo tanto computa, acumula y almacena internamente ese tiempo para después poderlo transmitir a través del puerto de salida OUT0. Y, por último, x3 que contiene el valor constante 1 generado internamente. Como en el caso anterior, en primer lugar generamos el programa bajo la forma de diagrama de flujo como vemos aquí a la derecha de la pantalla. Primero almacenamos el valor constante 1 en el elemento de la memoria x3 y luego queremos preguntar por el valor de reset. Y para ello leemos el valor de dicha señal sobre el elemento de memoria X0 y a continuación, preguntamos por el valor de ese elemento de memoria. Si X0 es mayor que 0, es decir, si es igual a 1 significa que RESET está activo y que por lo tanto debemos reinicializar nuestro cronómetro a 0. Recordemos que el elemento de memoria que guardaba el valor del cronómetro era X2. Y a continuación enviar el valor de esa memoria, de ese elemento de memoria, a la señal TIME a través del puerto de salida OUT 0 y una vez hechas estas dos acciones volveríamos al principio de nuestro algoritmo. Y aquí vemos que hay un bucle en el que mientras el RESET está activo, sea mayor que 0, es decir, sea mayor que 1, estaremos reinicializando nuestro cronómetro. Pero cuando RESET se desactiva, se hace 0, saldríamos por esta parte de esta sentencia condicional y entonces iríamos a preguntar por la señal START. Para ello leemos START sobre el elemento de memoria X0 y de nuevo una sentencia condicional en la que preguntamos por el valor de X0. Mientras X0 sea igual a 0 significa que START no se ha activado. Y por lo tanto volvemos al principio de nuestro algoritmo. Pero si X es igual a 1 entonces saldríamos por esta parte y iríamos a intentar y a arrancar nuestro cronómetro. Antes de ello debemos verificar que no se haya dado orden de parada a través del pulsador STOP y para ello lo que hacemos es leer el valor de STOP sobre X0 y de nuevo preguntamos por el valor de X0. Si X0 es uno, saldríamos por esta parte. Si X0 es mayor que cero, es 1, saldríamos por esta parte y de nuevo volvemos al principio de nuestro algoritmo. Pero si X0 es cero, es decir, si no hemos activado el pulsador STOP entonces venimos a esta parte del diagrama de flujo donde debemos ir detectando flancos positivos en nuestra señal de referencia externa. Y para ello si asumimos que dicha señal tendrá una forma periódica de este estilo donde vemos que es una señal periódica cuyo periodo es de una décima de segundo. Un semiperiodo la señal está a uno y otro semiperiodo, la señal está a cero. Lo que tenemos que ir detectando son flancos de subida de esta señal entre dos flancos de subida consecutivos, sabemos que ha transcurrido una décima de segundo. Para ello lo que hacemos es leemos la señal ref sobre X1 y preguntamos por el valor de X1. Si X1 es mayor que cero, significa que estamos en esta parte de aquí de la señal ref. Y entonces entraríamos en este bucle en el que nos quedamos mientras la señal ref sea uno. Es decir, durante todo este periodo de tiempo de aquí. Cuando se acaba la señal ref igual a uno, pasaríamos a REF=0 y en ese momento de esta sentencia condicionada saldríamos por la respuesta NO. Y en ese momento hemos detectado un flanco de bajada pero lo que buscamos es un flanco de subida luego ahora hay que seguir leyendo la señal ref sobre x1 para buscar este franco de subida. Leemos sobre x1 y preguntamos si x1 es mayor que 0, es decir, si x1 es 1. Y mientras no sea mayor que 0, mientras sea igual a 0, nos quedamos este bucle de aquí, que corresponde a ir explorando esta parte de la señal REF. Cuando salgan los de este bucle de aquí significará que hemos detectado este flanco de subida y que la señal REF ha vuelto a tomar el valor 1. Y por lo tanto cuando salgamos de este segundo bucle marcado en azul hemos pasado de 0 a 1 en la señal REF y hemos detectado un flanco de subida este que vemos aquí. Ello significa que ha transcurrido una décima segundo y por lo tanto debemos incrementar en 1 el valor de nuestro cronómetro que estaba almacenado en el elemento de la memoria interno X2. Una vez actualizado ese elemento de la memoria interno enviamos el valor de X2 a TIME, que es la señal externa que está conectada al puerto OUT0 y a partir de ahí volvemos a este bucle donde preguntamos si mientras tanto se nos va a activar el STOP. Si no se ha activado el STOP volvemos a entrar a la búsqueda de flancos de subida en esta parte que tenemos aquí. Y si se ha activado el STOP volvemos al principio de nuestro algoritmo. Y entonces mientras no se active una señal de RESET o una señal de START el valor de nuestro cronómetro está almacenado en el elemento de memoria X2 y se mantendrá el mismo valor al que hubiésemos llegado. Si se produce un RESET se reinicializaría el valor del cronómetro pero si se produce un START entonces seguiríamos acumulando tiempo en el cronómetro a partir del último valor almacenado. Y una última observación, la detección de flancos positivos en la señal de referencia funciona correctamente si y solo si el tiempo de ejecución de cada una de estas instrucciones que tenemos dentro de nuestro diagrama de flujo es muy inferior a un periodo de la señal de referencia. Es decir, muy inferior a una décima de segundo. Ahora como ejercicio vamos a tratar de generar el programa correspondiente al diagrama de flujo que acabamos de ver utilizando los tipos de instrucción que habíamos presentado y que aparecen aquí a la derecha. Para ello se deben asociar números a las distintas instrucciones del diagrama de flujo siguiendo unas reglas muy simples. En el caso de instrucciones secuenciales consecutivas como estas que tenemos aquí. Sí a una instrucción le corresponde el número i, al siguiente le corresponde el número i+1 y el programa correspondiente a este trozo del diagrama de flujo sería algo de este estilo. En el caso de salto condicional si a esa instrucción de salto condicional le corresponde la etiqueta i o el número i, entonces en el caso de que la condición no se cumpla, este caso de aquí, la instrucción a ejecutar llevará la etiqueta i +1. Mientras que si la condición se cumple, esta rama que tenemos aquí, tendremos que ir mediante un salto incondicional a la instrucción j. En este caso de toda esta parte de diagrama de flujo, el programa que se obtiene, el trozo o la porción de programa que se obtiene es esta de aquí. Tenemos la sentencia condicional, la sentencia siguiente que es la que se ejecuta cuando la condición no se cumple y la sentencia j que es a la que saltaremos cuando la condición sí que se cumple. Finalmente algunas veces tendremos que añadir alguna instrucción de salto incondicional cuando después de una instrucción con etiqueta i- 1. Lo que viene a continuación no es la siguiente en secuencia sino otra instrucción que está en otra parte de nuestro programa. En ese caso, la instrucción siguiente a la i- 1, tendrá la etiqueta i y será una instrucción como vemos aquí abajo, un salto incondicional a la instrucción que de verdad hay que ejecutar que es la j. Es decir, una situación de este estilo da lugar a esta estructura de programa. En todos estos casos la j puede ser menor o mayor que la i. En base a las directrices o reglas que acabamos de dar esta es una posible solución al problema planteado de etiquetar el diagrama de flujo del algoritmo del cronómetro. Aquí tenemos nuestro diagrama de flujo, al siguiendo esas reglas hemos seguido dando etiquetas a las distintas instrucciones y pueden haber distintas soluciones igualmente válidas. Y ahora, os quería a vosotros verificar que la asignación del número de etiquetas o instrucciones es correcta. Y que por lo tanto, a partir de esta asignación que tenemos en el diagrama de flujo de la derecha obtenemos automáticamente el programa que hay a la izquierda de la diapositiva. Y con esto hemos acabado esta sesión que podemos resumir diciendo que se ha definido parcialmente un sistema genérico de procesador y que cuenta con recursos de entrada, salida de cómputo y de almacenamiento de datos. Y que es capaz de realizar una serie de instrucciones simples que constituyen el conjunto de instrucciones de un citado procesador. Este procesador permite implementar diferentes algoritmos, mediante grupos específicos de dichas instrucciones básicas, a los que hemos llamado a esos grupos de instrucciones que realizan un determinado algoritmo, les hemos llamado programa. Y cada programa es una materialización concreta de un determinado algoritmo, pudiendo a su vez existir distintos programas que materialicen un mismo algoritmo. Y mediante estas técnicas hemos detallado la realización de dos ejemplos de algoritmos distintos. El control de temperatura y el del cronómetro sobre un único procesador genérico. Y esto es todo por hoy, muchas gracias y hasta la próxima.