En este primer tema de la segunda semana vamos a ver los circuitos combinacionales.
Vamos a ver qué son y vamos a ver
un par de formas de diseñarlos que posteriormente iremos refinando.
¿Qué es un circuito combinacional?
Es un circuito que cumple dos condiciones.
La primera hace referencia a que las entradas
y las salidas sólo pueden tomar los valores
cero y uno, y esto es lo que significamos
cuando decimos que implementan una o varias funciones de conmutación.
La segunda condición hace referencia a la dependencia de la salida respecto
de las entradas, y dice que el conjunto de valores que toman las
salidas del circuito en un instante de tiempo t dependen única y exclusivamente
del valor que toman las entradas en ese mismo instante de tiempo t.
Un poco más formalmente, podemos decir que un circuito combinacional
es un circuito que en cada una de sus salidas asocia
un valor cero o uno a cada una de las posibles combinaciones de las entradas.
Vamos a ver un primer circuito combinacional.
Vamos a construir un circuito capaz de sumar dos números x e y de cuatro bits.
Los números, el número mayor que podemos representar
con cuatro bits es el 1, 1, 1, 1.
Si sumamos los dos números mayores que podemos obtener por x e y, tenemos este
resultado que como vemos necesita como máximo
cinco cifras binarias para poder representar el resultado.
Estos son estas cinco cifras que tenemos aquí.
Lo que vamos a hacer es, a la cifra
más significativa la voy a llamar acarreo de salida y además voy a permitir
que me entre un acarreo de entrada que sólo puede tomar los valores 0 y 1.
El hecho de definir este acarreo de entrada
y este acarreo de salida, nos va a permitir
más adelante utilizar este módulo para concatenar
varios de ellos para construir sumadores de números mayores.
Bien, sabiendo todo esto,
ahora formalmente podemos definir el sumador: Dados los números
x e y de cuatro cifras binarias, de cuatro bits.
El sumador va a calcular la suma de x + y + el acarreo de entrada y va a
poner el resultado en los cuatro bits z3 z2, z1y z0, más el acarreo de salida.
Esto sería la descripción funcional, donde lo único que hemos de tener
en cuenta es que todas la operaciones se realizan en base dos.
La ventaja de trabajar con circuitos en los que las entradas y salidas sólo
pueden tomar valores discretos, es que podemos describir
el funcionamiento de un circuito exhaustivamente a través
de una tabla, en la que pongamos todas las entradas x3,
x2, x1, x0; y3, y2, y1, y0, y el acarreo de entrada
que me permitiréis escribirlo como a_in
simplemente para ser más rápidos ¿vale?
Y que en la parte derecha de la tabla vamos a poner las salidas, el acarreo de
salida que lo voy a llamar a_out, y los cuatro valores z3, z2, z1, z0.
Y aquí iremos poniendo ..., si todas las entradas son 0,
según la descripción funcional que tenemos aquí, las salidas han de ser 0.
Si las entradas son 0 y el acarreo de entrada es 1, la salida será 1.
Así sucesivamente.
Por ejemplo, si la entrada es 7 y 6 en decimal, y el acarreo de
entrada es 0, la suma es 13, que
quiere decir acarreo de salida igual a 0.
Y 1, 1, 0, 1, que es el 13 en binario.
Y finalmente
en el último caso si sumamos 1, 1, 1, 1, con 1, 1, 1, 1,
y con acarreo de entrada igual a 1, pues obtendremos un resutlado de todo 1s.
Esta es la misma tabla, mejor escrita.
Vamos a utilizar una memoria ROM para implementar esta tabla.
Un breve recordatorio de lo que es la memoria ROM
que ya vimos en la semana anterior, ¿vale?
La memoria ROM es un dispositivo
capaz de guardar en filas una serie de informaciones. Cada
una de estas filas recibe el nombre de palabra, y se le asocia una dirección.
Esta sería la palabra de dirección 0, dirección 1, etcétera, hasta dirección m.
De manera que en las entradas se pone la dirección
y en las salidas se obtiene el contenido de la palabra cuya dirección
se ha puesto en las entradas, es decir, por ejemplo
si yo pongo aquí 0, 0, 0, 1, la palabra
de dirección 1; el contenido de la palabra de dirección
1 es el que se volcará a la salida, ¿vale?
Bueno, de acuerdo.
Pues vamos a utilizar ahora una memoria ROM para implementar esta tabla.
La memoria ROM, lo que voy a hacer es utilizar las combinaciones de
entrada como direcciones, es decir, las entradas a la memoria
ROM van a ser todas las entradas al circuito x3, x2, ... , x0, y3, ...
bien, todas las entradas, y0 y el acarreo de entrada.
Y entonces voy a poner, por ejemplo, en la
dirección 9 que sería esta, en la palabra de dirección 9
voy a poner precisamente el valor de las salidas
del circuito cuando las entradas son 9 en binario.
Voy a poner el 0, 0, 1, 0, 1, ¿de acuerdo?
Es esto lo que colocamos aquí.
Y esto lo vamos a hacer pues, con todas las palabras de la
memoria, es decir, en la palabra de
dirección 0, yo voy a poner el contenido
de las salidas cuando las entradas son 0.
En este caso son todos 0, etcétera.
Y las salidas serán el a_out z3, z2, z1, z0 z
La idea es que toda
la parte de la tabla correspondiente a las salidas tal
cual se vuelca en la memoria ROM y se utilizan las entradas para direccionar
la salida correspondiente en cada momento. Aquí tenemos la
memoria ROM que implementa esta tabla. Fijaros
por ejemplo, si yo cojo la dirección 2 y pongo estos
valores por las entradas, lo que va a hacer esta memoria ROM es devolverme
la palabra de dirección 2, que es esta, y que coincide
exactamente con la salida cuando las entradas son las que yo le acabo de poner.
Fijémonos que puesto que tengo nueve
entradas y aquí yo he escrito todas las posibles combinaciones de 0s y
1s, esta tabla tiene un total de 2 elevado a 9,
de 512 filas, y por lo tanto necesito
una memoria que contenga 512 palabras.
Como además cada una de las palabras han de guardar la información sobre todos
los bits de salida, necesito que cada una de estas palabras tenga cinco bits.
Bueno, fijaros que esto es muy potente, porque lo que estoy
diciendo es que yo puedo construir
cualquier circuito combinacional de n entradas
y m salidas, utilizando una memoria ROM de
2 elevado a n palabras, cada palabra conteniendo m bits.
Y con esto puedo construir, insisto, cualquier circuito combinacional.
El problema de esta aproximación, el inconveniente, es que habitualmente es
ineficiente, en el sentido de que puedo construir el mismo circuito utilizando
menos recursos si en vez de utilizar una memoria ROM utilizo puertas lógicas.
Y esto es lo que vamos a ver a continuación.
Primero os dejo con una pequeña pregunta para que contestéis.
Bien, pues ahora vamos a ver
como construir circuitos combinacionales utilizando puertas lógicas.
Veremos una primera aproximación que más adelante refinaremos.
Estamos con el mismo circuito.
Antes de empezar a diseñarlo, lo que nos podemos dar
cuenta es que este circuito yo lo puedo construir concatenando cuatro
circuitos más pequeños como este, capaces de sumar dos números x_i
y y_i de un bit con un acarreo.
Esto es totalmente generalizable, de manera que si yo en vez de
en vez de construir un sumador de números de cuatro bits quiero
construir un sumador de números de seis
bits, tendría que poner aquí dos módulos más.
Es un diseño modular totalmente generalizable.
Nos vamos a centrar en este pequeño circuito de aquí.
Dados x_i, e y_i, y el acarreo de entrada, si yo sumo por
ejemplo 1 más 1 con un acarreo 0, la suma en base dos me da 1, 0.
Este me aparece por z_i, y este 1 lo
tomo como un acarreo de salida que me va a parar al siguiente módulo.
Si yo sumo 1, 1, 1, la suma en binario es 1, 1.
Este 1 me aparecería por z_i, y este me aparecería por el acarreo de salida.
Vamos a ver como diseñar exactamente este
módulo de aquí. Otra vez. ... vamos a proceder igual.
Como es un circuito en que las entradas y salidas están limitadas a 0,1,
puedo representar su comportamiento por
una tabla donde, como entradas, tendré el
x_i, la y_i y el acarreo de entrada. En
este caso, y a partir de ahora, lo vamos a llamar c_i.
La c viene de la palabra anglosajona "carry",
que es lo mismo que acarreo, ¿vale?.
Y como salidas tendremos el acarreo de salida, que lo llamaremos c_o y z_i.
Y aquí tenemos el conjunto de todas las
combinaciones, esto es un poco más sencillo.
Si las entradas son 0, 0, 0, la suma es 0.
Si las entradas son 0, 0, y el acarreo de entrada es 1, la suma es 1.
Si sumamos
el 0 con el 1 con acarreo de entrada 0 nos vuelve a dar 0, 1, etc.
Así podríamos ir repasando todos los valores.
1, 0, 1, 1.
teniendo en cuenta que en el fondo yo estoy obteniendo los valores
de salida siguiendo el algoritmo de la descripción funcional del circuito.
Esta es la
tabla ¿vale?
Vamos a ver como lo podemos construir con puertas lógicas.
Os recuerdo que definimos la semana pasada tres tipos
de puertas, la AND, la OR y el inversor.
Decíamos que la puerta AND genera un 1 en la
salida si y sólo si sus dos entradas son 1,
de hecho, genera un 1 si la x y la y, de ahí el nombre de AND, toman
el valor 1. La puerta OR genera un 1 a la salida si
por la x o por la y, de ahí el nombre de OR, le está entrando un 1,
es decir, el funcionamiento es este.
Y el inversor cambia 0s por 1s y 1s por 0s.
Si entra un 0 aparece un 1 a la salida y vice versa.
Aquí tengo la misma tabla de verdad.
Vamos a ver, primero vamos a construir la salida, construir
un circuito que me genere la salida c_o.
Lo primero que necesito darme cuenta es de que yo necesito que
c_o tome el valor 1 cuando por sus entradas x_i,
y_i, y c_i me aparece, o
0, 1, 1, o 1, 0, 1, o 1, 1, 0, o 1, 1, 1.
Puedo utilizar una puerta AND a
la que le entre el x_i
convenientemente pasado por un
inversor; la y_i y la c_i.
La salida de esta puerta toma el valor uno si y sólo si la combinación
de entrada es 0, 1, 1.
Para cualquier otra combinación de entrada,
el valor de salida de esta puerta será 0.
Puedo hacer lo mismo con la combinación 1, 0, 1.
No pongo ya las entradas, están siempre en el mismo orden.
Puedo hacer lo mismo para la combinación 1, 1, 0.
Y puedo hacer lo mismo para la combinación
1, 1, 1. Bien.
Yo necesito que c_o tome el valor 1 si
aparece como entrada esta combinación o esta, o esta, o esta.
Es decir, si a la salida de esta puerta AND tengo 1 o a la salida de
la segunda puerta AND tengo un 1 o a la salida de la tercera puerta AND tengo
un 1 o a la salida de la cuarta puerta tengo 1.
Y esto lo puedo hacer precisamente con una puerta OR.
Si yo entro las salidas de las puertas AND a una
puerta OR aquí tengo una salida que va a tomar el
valor 1 en las mismas
condiciones que tiene que tomar el valor uno la salida c_o