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