martes, 28 de enero de 2014

5. Diseño de circuitos MSI: MUX y Codificador


Empezaremos esta entrada con el diseño de algunos circuitos MSI fundamentales para, a través de ellos, estudiar nuevos conceptos en el honorable arte de la codificación VHDL. En primer lugar, un circuito MSI es un sistema digital a mediana escala, de allí las siglas Medium Scale Integration, haciendo referencia a la densidad de compuertas lógicas que integran el sistema. Si bien, esta cantidad de compuertas lógicas aun no alcanza la densidad de componentes que encontraríamos en un microprocesador, los MSI presentan suficiente complejidad como para distraer nuestra mente por un buen rato, claro que ello depende de que tantos estilos y trucos tengamos bajo la manga. Empezaremos con un multiplexor de 2 bits y dos entradas.

Diseño del MUX2to1


Gracias a nuestros buenos conocimientos en los circuitos digitales, sabemos que un multiplexor es un sistema digital cuya salida es elegida de entre dos o más entradas, de acuerdo a una entrada de selección.

Un multiplexor, nuestro primer MSI.


En este caso, la entrada A sería nuestro vector de bits A1A0, y la entrada B, B1B0. La entrada de selección S será quien decida qué vector de entrada aparecerá en nuestra salida. 

Con no mucho esfuerzo podemos crear el sistema utilizando compuertas básicas, las ya conocidas AND, OR, NOT. El esquema sería, para el caso expuesto, el siguiente:

Lógica cableada para describir un multiplexor, ¿la solución más directa?

A partir de este esquema, podríamos obtener un código en VHDL, describiendo cada compuerta mostrada y sus conexiones. Dicho código lo presento a continuación:
library ieee;

use ieee.std_logic_1164.all;
 
entity MUX2to1 is
port(
A :in std_logic_vector (1 downto 0);
B :in std_logic_vector (1 downto 0);
S :in std_logic;
Z :out std_logic_vector (1 downto 0));
end MUX2to1;

architecture arqMUX21 of MUX2to1 is
begin
Z(1)<=(A(1) and not S)or(B(1) and S);
Z(0)<=(A(0) and not S)or(B(0) and S);
end arqMUX21;
Tras compilar, verificamos si el diseño se comporta como deseamos, utilizando el simulador mediante el waveform file.

Los resultados se muestran en la siguiente imagen, y se puede ver que el diseño cumple su cometido perfectamente.

Diagrama de tiempos resultado de verificar el diseño. Se comporta de acuerdo a lo esperado.

Y con esto, habremos acabado el diseño nuestro primer circuito MSI.

Quizá pensemos que buscar otros medios para implementar este multiplexor no parece justificado, si la implementación directa de las ecuaciones booleanas parece suficiente, como se hizo líneas arriba. El problema radica al incrementar la anchura del bus de los datos de entrada. Para entradas de 4 bits, las ecuaciones obtenidas se complicarían al borde de quedar completamente incomprensibles. Si incrementamos el ancho de entrada a 8 bits, tendríamos 16 bits para los datos de entradas, más un bit se selección, de los cuales nuestra salida dependería de una manera que desanimaría a cualquiera. Y para agregarle una cereza al pastel, imagínese tener 4 entradas en lugar de 2, cada una de 8 bits. Este es el sistema que implementaremos ahora a través del VHDL y los métodos de programación que hasta ahora conocemos, y si no los conocemos, que aprenderemos ahora mismo. 

En este caso, plantear el diseño de la manera en que se hizo inicialmente nos llevaría a escribir sentencias de asignación muy largas para cada bit de salida de Z (en este caso, Z tiene 4 bits). Una manera más sencilla es manejando las entradas como lo que son, como vectores de bits, y utilizando otro tipo de sentencia. A continuación les muestro el código, y tras él, explicaré algunos puntos que quizá puedan confundirlos. 
library ieee;

use ieee.std_logic_1164.all;

entity MUX4to1 is
port(
A :in std_logic_vector(3 downto 0);
B :in std_logic_vector(3 downto 0);
C :in std_logic_vector(3 downto 0);
D :in std_logic_vector(3 downto 0);
S :in std_logic_vector(1 downto 0);
Z :out std_logic_vector(3 downto 0));
end MUX4to1;

architecture arqMUX41 of MUX4to1 is
begin
with S select
 Z <= A when "00",
 B when "01",
 C when "10",
 D when "11",
 null when others;
end architecture;
Tras simular, obtenemos las siguientes formas de onda, que verifican nuestro diseño. 


Resultados de simulación de nuestro diseño del MUX4to1.

Aclaraciones respecto al código, me parece que no hay muchas por hacer. Se manejaron todos los datos como vectores de bits, lo cual permitió reducir la codificación al manejarlos por grupos. La estructura de selección usada en la arquitectura es la siguiente:


with entrada_selectora  select


            salida    <=    seleccion1 when valor_1,


                            seleccion2 when valor_2,


                           


                            seleccionn when valor_n;


Esta estructura permite asignar salidas de acuerdo a una entrada de selección, justo como lo hace un multiplexor. En este caso se asignaron las entradas A, B, C o D a la salida Z de acuerdo al valor binario de A. Notar que solo la última asignación irá con punto y coma al final, mientras que las primeras solo serán finalizadas por comas. Un punto interesante en este caso sería la última asignación. La palabra reservada others se utiliza para referenciar a cualquier valor no utilizado en la asignación, y en este caso, si no se utiliza, nos lanzaría errores. Y ello ¿por qué? Para que funcione correctamente esta estructura de selección, deberán escogerse entre todos los posibles valores de la entrada entrada_selectora. Pero, ¿acaso no se mencionaron todos los posibles valores de la entrada S en el ejemplo? Veamos, con dos bits, usted dirá confiadamente que todos los posibles valores son “00”, “01”, “10” y “11”. Quizá para cualquier estudiante de circuitos digitales esa respuesta sea correcta, pero en estos mares de diseño, usted está equivocado. ¿Y qué valores nos faltan? Pues tendríamos “ZZ”, “XX”, “UU”, entre otros. Estos valores son todos los otros valores soportados por el tipo de entrada std_logic (eso ya lo vimos anteriormente). Como muchos de estos valores no tienen un significado físico exacto y solo son tipos de apoyo para la simulación, no es necesario recordarlos y referenciarlos todos. Imagínese tener que referenciar todas las posibles combinaciones entre ‘1’, ‘0’,  ‘x’, ’U’, ’Z’, etc. Para dos bits tendríamos más de 10 combinaciones posibles.  Ahora, si fueran 4 bits, si fueran 8. Se ve la necesidad de referenciar todos estos valores que no se utilizan directamente en el problema, y para ello se utiliza la palabra reservada others. Así, para asignaciones, basta con referenciar los valores de selección que queramos acorde a nuestro problema, y el resto, referenciarlos con others. En este caso, asignando a la salida null para los valores others, hacemos que el compilador ignore estos valores y solo tome en cuenta los valores referenciados directamente.

Así, hasta, podríamos decir que tenemos las herramientas para diseñar los multiplexores que queramos, sea cual sea la cantidad de entradas y su ancho de bits, de acuerdo a la FPGA o CPLD que tengamos disponible. El siguiente sistema MSI a diseñar será el famoso codificador.


Diseño del Code8to3



Otros famosos sistemas MSI son los codificadores. Como su nombre lo indica, estos sistemas generan en su salida un determinado código binario de acuerdo a los pines de entrada que estén activados. Para este caso, diseñaremos un codificador de 8 a 3 con prioridad al bit de mayor peso. Es decir, se tendrán 8 entradas, I7I6I5I4I3I2I1I0, de las cuales, dependiendo quien esté activa, nuestra salida Z generará el número binario del bit activado. Por ejemplo, si nuestra entrada fuese 00010000, entonces el bit activado es I4, y nuestra salida Z deberá tomar el valor de 100 (4 en binario). La condición de prioridad de aplica cuando se tienen más de una entrada activa. En tal caso, solo se codifica el bit activado de mayor peso. Por ejemplo, si nuestra entrada fuese 01001001, nuestra salida Z sería 110. 


El famoso codificador de 8 a 3.

Ahora, el para implementar este sistema en VHDL procederemos a aplicar una nueva estructura de programación denominada proceso. Para explicar qué es un proceso, primero presentamos el código de nuestro codificador, y con él hacer las explicaciones necesarias.
library ieee;

use ieee.std_logic_1164.all;

entity Code8to3 is
port(
data_in : in std_logic_vector(7 downto 0);
z : out std_logic_vector(2 downto 0));
end Code8to3;

architecture arqcod83 of Code8to3 is
begin

process(data_in)
begin
 if data_in(7)='1' then
  z<="111";
 elsif data_in(6)='1' then
  z<="110";
 elsif data_in(5)='1' then
  z<="101";
 elsif data_in(4)='1' then
  z<="100";
 elsif data_in(3)='1' then
  z<="011";
 elsif data_in(2)='1' then
  z<="010";
 elsif data_in(1)='1' then
  z<="001";
 else z<="000";
 end if;
end process;

end arqcod83;

Los resultados de simular nuestro diseño se muestran en la siguiente imagen, en la que se verifica que éste funciona correctamente.


El resultado satisfaze nuestras expectativas, el diseño cumple con lo requerido.

Ahora veamos qué cosa es este proceso. Estas estructuras de programación son las llamadas secuenciales, pues las instrucciones que se encuentren en él se ejecutarán una a continuación de otra, de manera secuencial, como lo haría un programa de C++ o JAVA. El modelo de todo proceso es el siguiente:


process (lista_sensitiva)
begin
instrucción_1;
instrucción_2;
instrucción_n;
end process;


En este proceso, la instrucción 1 se ejecutará primero, luego la instrucción 2, y así, secuencialmente hasta ejecutarse la instrucción n. Otro punto a resaltar es que todo proceso deberá declararse dentro de una arquitectura. Y, ¿qué es la lista sensitiva? Pues la lista sensitiva es el conjunto de señales que activarán al proceso.
Un proceso se activa cuando cualquier señal de su lista sensitiva cambia de estado. En el ejemplo, la lista sensitiva está formada por el vector data_in de 8 bits. De esta forma, cuando cualquier bit de dicha palabra cambie de estado, se activará el proceso con el código descrito. Ahora, el concepto de prioridad es implementado fácilmente usando las famosas condiciones if-then-else. Colocamos en el if más externo la condición de mayor prioridad, de manera que ésta se evalúe primero. 

Antes de dar por terminada esta entrada, mencionarles que las señales y salidas solo actualizarán sus valores cuando el sistema entre en estado inactivo. En el caso del codificador diseñado la salida z se actualizará en el preciso momento en que el proceso quede inactivo, cuando no se detecte cambio en los niveles de entrada de la lista sensitiva.

No hay comentarios:

Publicar un comentario