CT14: Buses: medio compartido - Obijuan/Cuadernos-tecnicos-FPGAs-libres GitHub Wiki
Descripción
Ejemplos y bloques para la organización de circuitos que comparten información a través de un BUS. Creación de buses UNIDIRECCIONALES y BIDIRECCIONALES
- Icestudio: Todos los ejemplos se han probado con Icestudio 0.12. Usa esta versión o superior
- Colecciones:
- iceBus: Elementos para la creación y el acceso a Buses
- Ejemplos: Todos los ejemplos de este cuaderno técnico están accesibles en su repositorio en github
Historial
- 2023-Enero-17: Version inicial del cuaderno técnico
- 2024-Mayo-24: Ejemplos adaptados a la nueva toolchain: apio-0.9.4. Eliminado el error en la verificación. Probados con icestudio 0.12. Los pantallazos de los ejemplos no se han actualizado todavía
Contenido
- Introducción
- Compartiendo recursos
- Clasificación de los buses
- Bus Unidireccional
- Bloque de acceso al BUS
- Ejemplo 4: UNIBUS: parpadeo del LED a diferentes velocidades
- Ejemplo 5: UNIBUS: Tocando dos tonos
- Ejemplo 6: UNIBUS: Mostrando números en los LEDs
- Bus de datos y bus de control
- Ejemplo 7: UNIBUS: Compartiendo un registro
- Bus con campos
- Ejemplo 8: UNIBUS: Compartiendo un registro. Implementación 2
- Ejemplo 9: UNIBUS. Compartiendo un registro. Implementación 3
- Ejemplo 10: UNIBUS. Compartiendo un registro. Implementación 4
- Bus Unidireccional con Feedback
- Ejemplo 11: Controlador falso (Fake controller)
- Ejemplo 12: Controlador falso. Implementación II
- Ejemplo 13: Secuencia en los LEDs
- Ejemplo 14: Dos secuencias en los LEDs
- Ejemplo 15: Dos secuencias en los LEDs. Implementación 2
- Unificación en un BUS
- Ejemplo 16: Dos secuencias en los LEDs. Implementación 3
- Ejemplo 17: Dos secuencias en los LEDs. Implementación 4
- Bus bidireccional
- Conclusiones
- Autor
- Licencia
- Créditos y agradecimientos
- Enlaces
Introducción
En general, los circuitos están formados por unidades funcionales (bloques) interconectados entre sí mediante grupos de cables llamados buses
Un ejemplo es un computador básico, formado por los siguientes tres elementos: la unidad central de proceso (CPU), la memoria principal y el controlador de entrada/salida. En este diagrama se muestra su modelo estructural:
NOTA: Este dibujo está tomado de la lección 1 (Símplez) del libro "Conceptos básicos de arquitectura y sistemas operativos" por Gregorio Fernández. En él se describe el modelo estructural de un computador simplificado, denominado Símplez. Este libro me marcó mucho cuando estudié por primera vez la asinagura de Fundamentos de los computadores en la Universidad. Y además, tuve el enorme privilegio de tener como profesor a su autor 🙂️
En este esquema simplificado aparecen 3 unidades funcionales: la CPU, la memoria principal y el controlador de E/S. Están unidos mediante 3 buses, llamados Bus A, Bus C y Bus D. El Bus A transporta las direcciones, el Bus D los datos y el Bus C las señales de control (microórdenes)
En general, cada unidad de los circuitos genera una información de salida y recibe una información de entrada. A las unidades que generan datos las llamaremos productoras y a las que reciben datos consumidoras. Una unidad puede ser productora, consumidora o ambas
En un circuito siempre habrá uno o varios consumidores (es indifierentes). Sin embargo, el número de productores nos permite establecer la siguiente clasificación:
- Circuitos con un único productor
- Circuitos con múltiples productores
Compartiendo recursos
Un caso muy frecuente es cuando tenemos varios productores y un único consumidor. Por ejemplo cuando el consumidor es una unidad de salida, como por ejemplo un puerto serie, un LCD, o simplemente unos LEDs. Los productores envían sus datos a la salida (consumidor), a través del bus, para mostrársela al usuario
Como el recurso es único y compartido, ambos productores NO PUEDEN USARLO A LA VEZ. Tiene que existor otro elemento (que llamamos unidad de control, o controlador central) que sea el que decida en cada momento qué productor envía los datos al consumidor
Además, en electrónica digital NO SE PUEDEN CONECTAR DOS SALIDAS al mismo cable, o de lo contrario se produce un cortocircuito. Es necesario añadir un elemento para acceder al bus
Para entender estos conceptos de una manera empírica, vamos a definir un sistema mínimo, en el que usaremos el bus más pequeño posible: un bus de un bit. Nuestro consumidor será un simple LED. Y los productores serán circuitos que generan una secuencia en el LED
Ejemplo 1: Un LED parpadeante
Comenzamos por un ejemplo cuyo funcionamiento ya conocemos,y hemos usado muchas veces: un circuito que hace parpadear un LED. Pero ahora lo vamos a analizar desde otra perspectiva
El LED es el recurso a usar, que es un consumidor: El bit recibido se muestra por el LED. Hay una unidad productora, que es la que genera el parpadeo, que se transmite por el bus de 1 bit al LED
Es un circuito con un productor y con un consumidor
El LED parpadea a la frecuencia de 1 Hz indefinidamente
Compartiendo el bus
Si ahora queremos compartir el LED con otro circuito productor hay que añadir un elemento nuevo que nos permite acceder al bus. Este elemento es un multiplexor 2 a 1
Cuando la señal sel
del multiplexor está a 0 (en reposo), es la entrada 0 la que llega al LED. El productor conectado a la entrada 0 es el que tiene acceso al recurso. Cuando sel
se pone a 1, es el otro productor, el conectado a la entrada 1, el que tiene acceso al recurso
Esta entrada de selección la maneja el controlador, que es el circuito que decide qué productor tiene acceso al LED a través del bus en cada momento
Ejemplo 2: LED apagado y parpadeante
En este circuito de ejemplo ampliamos el ejemplo para permitir que el LED esté en dos estados diferentes: Bien parpadeando, cuando el corazón (productor) tiene el BUS, o apagado, cuando el bit 0 está en el bus
El bit a 0 es el productor por defecto: Tiene el bus cuando ningún otro productor está accediendo
(02-LED-apagado-parpadeante.ice)
El controlador es manual: un pulsador. Cuando No está apretado, el LED está apagado. Cuando se aprieta, el LED parpadea: el corazón accede al BUS y toma el control del LED
Al apretar el botón se genera la microorden blink
, que es la que hace parpadear el LED (inyectando la salida del corazón en el bus)
Ejemplo 3: LED en tres estados
Ampliamos el ejemplo anterior para hacer que el LED parpadee a dos velocidades. Al apretar el pulsador SW1
el LED parpadea a 1 Hz, al apretar el pulsador SW2
lo hace a 4 Hz y si no se aprieta ninguna el LED permanece apagado
Ahora el controlador, que sigue siendo manual, genera 2 microórdenes: blink1
y blink4
. Cada una de estas señales da acceso al bus al productor corresponiente: al corazón de 1Hz y de 4Hz
Observamos que los multiplexores están conectados en cascada. El bus lo forma el cable que entra por las entradas 0 de los multiplexores. Los productores se conectan por las entradas 1, y vuelcan su información al bus cuando se activa la microorden correspondiente
Esta forma de acceso al bus tiene una ventaja: tiene prioridad. En caso de activarse las dos microórdenes a la vez, el LED parpadea a 1Hz. El multiplexor más cercano al recurso es el que define la prioridad. Cuando más cerca está el productor del recurso, más prioridad tiene. El bit a 0 es el de menor prioridad: sólo tiene el bus si no hay microórdenes activadas
Esta estructura tiene otra ventaja: es escalable. Si ahora quisiéramos añadir más productores, basta con conectarlos a través de su multiplexor de acceso corresopndiente
Clasificación de los buses
Los buses que vamos a ver en este cuaderno técnicos los clasificamos de la siguiente forma:
- Bus unidireccional
- Bus unidireccional con Feedback
- Bus bidireccional
Bus unidireccional
Es el bus más simple, y uno de los que más usaremos. Está formado por un único recurso (consumidor) compartido por varios productores. El caso de uso más sencillo es el emplear LEDs para mostrar información de salida. Todos los circuitos que precisen mostrar información en los LEDs, lo harán a través de un BUS Unidireccional
En esta figura se muestra un ejemplo de un bus unidireccional de N bits en el que hay dos circuitos productores y un consumidor. Además está el productor por defecto que define el valor que tiene el bus cuando no hay otro circuito accediendo a él
El sentido de la información es en una única dirección: de los productores al consuidor. El consumidor se sitúa en el extremo derecho, y los productores en la izquierda
El bus es el cable azul, que une el productor por defecto con el consumidor. Este cable atraviesa los elementos de acceso al bus, que normalmente son "transparentes"
Cuando un productor necesita enviar algo al consumidor, activa su señal de acceso al bus oe
y su dato (de N bits) entra en el bus y llega al consumidor
Este bus tiene prioridad: El productor situado más a la derecha tiene una prioridad más alta
En este bus NO HAY ARBITRAJE. El controlador que genera las microórdenes de acceso es el que decide qué productor accede al BUS y qué otros productores no. Este bus está pensado para aplicaciones de control en bucle abierto
Bloque de acceso al bus
Para acceder al bus se utiliza el bloque Unibus-access, que se encuentra en la colección iceBus
El bus de N bits se introduce por la izquierda y sale por la derecha para conectarlo con el resto de elementos. Por defecto, el bus atraviesa el componente (es un componente transparente)
Por la entrada dat
el productor introduce el dato de N bits a inyectar en el bus. La señal de control oe
se usa para inyectar el dato. Cuando oe
es 0, el componente funciona en modo transparente. Cuando oe
es 1, el dato de N bits que está en la entrada dat
se inyecta en el bus
Este bloque de acceso se implementa mediante un multiplexor, como hemos visto en las secciones anteriores. En esta figura se muestra la implementación de un bloque de acceso a un bus de 2-bits:
Se utiliza un bloque nuevo, en vez de directamente el multiplexor, para aportar información semántica: Este bloque es para inyectar datos en el bus. Da igual cómo esté implementando. De esta forma, los diseños quedan más claros, y los buses creados son más legibles
Ejemplo 4: UNIBUS: Parpadeo del LED a diferentes velocidades
Para familiarizarnos con este bus, veremos diferentes ejemplos, comenzando por el más sencillo de todos: un bus de 1 bit. En este ejemplo se crea un bus de 1-bit con el valor por defecto 0. Hay dos productores que inyectan en el bus señales de 2Hz y 10Hz al apretar los botones sw1
y sw2
respectivamente
(04-UNIBUS-LED-tres-estados.ice)
El valor por defecto está en la izquierda. Mediante dos bloques de acceso los productores 1 y 2 inyectan sus valores en el bus cuando se activan las microórdenes blink1
y blink2
respectivamente
La prioridad la tiene el productor 1 (Corazón de 2Hz). Si se activan a la vez las dos microórdenes, el productor 1 será el que tenga el LED
En este ejemplo vemos cómo ahora podemos definir buses para compartir recursos (en este ejemplo el LED) y cómo los circuitos que los usan (productores) están desacoplados. Añadir nuevos productores es sencillo, así como modificar los existentes. Al estar desacoplados los podemos modificar sin influir en el resto de los circuitos conectados al bus
Ejemplo 5: UNIBUS: tocando dos tonos
En este ejemplo el recurso a compartir es un zumbador. Hay dos circuitos productores que emiten tonos de 1Khz y 2Khz respectivamente. El valor por defecto del bus es 0 (Silencio)
Se emiten secuencialmente un tono de 1Khz, luego de 2Khz, luego un silencio y se vuelve a repetir cíclicamente, a la velocidad de 10 Hz
(05-UNIBUS-tocando-dos-tonos.ice)
El controlador usado es un anillo de 3 biestables D, en los que hay un bit a 1 que se mueve cíclicamente. Al pasar por dos de los biestables se generan las microórdenes tone1
y tone2
Ejemplo 6: UNIBUS: Mostrando números en los LEDs
En este ejemplo se muestran 3 números distintos en 8 LEDs. Para ello se utiliza un bus de 8 bits. El controlador es manual, construido a partir de 4 biestables D en anillo. Cada vez que se aprieta el pulsador SW1
se desplaza el bit y se genera la siguiente microorden
(06-UNIBUS-mostrando-numeros-LEDs.ice)
En los LEDs se muestra la secuencia 0x00
, 0x55
, 0xAA
y 0xFF
Bus de datos y bus de control
Los controladores tienen una entrada para los datos y una o varias para las señales de control. Para poder compartir estos controladores, lo que hacemos es que creamos dos buses, una para llevar los datos y otro para las señales de control. Y reciben los nombres de bus de datos y bus de control
El productor que quiere acceder al controlador deberá escribir los datos en el bus de datos y activar las señales necesarias del bus de control
En la siguiente figura se muestra esta idea, usando dos productores y un consumidor
Ejemplo 7: UNIBUS: Compartiendo un registro
Para entender cómo acceder a los controladores, utilizaremos como ejemplo un registro de 5 bits. Hay dos circuitos productores que escriben en el registro los valores 0x03
y 0x1F
al apretar los pulsadores SW1
y SW2
respectivamente.
La salida del registro está conectada a 5 LEDs para poder ver el valor almacenado.
Utilizamos un bus de datos de 5 bits para llevar los valores, y un bus de control de 1 bit para la señal de load
del registro
(07-UNIBUS-compartiendo-registro.ice)
Bus con campos
Por un bus podemos llevar información estructurada, o divida en sub-partes. Por ejemplo, para acceder a un controlador, o al registro del ejemplo anterior, podemos llevar por un mismo bus tanto los datos como el control. Esto simplifica los diseños, al no tener que usar dos buses separados sino uno con dos campos
Para agrupar los campos en un bus usamos bloques Join, y para separarlos bloques Split
Ejemplo 8: UNIBUS: Compartiendo un registro. Implementación 2
Este ejemplo es similar al 7, pero usando un único BUS de 6 bits con dos campos, uno de datos de 5 bits y un bit adicional para la señal load
Los dos productores agrupan el dato y la señal de load
, usando un bloque join 5-1 para introducirlo en el bus. Al final del bus, en la derecha, se usa un bloque Split 5-1 para separarlos y conectarlos al registro
(08-UNIBUS-compartiendo-registro-II.ice)
Ejemplo 9: UNIBUS: Compartiendo un registro. Implementación 3
Para simplificar nuestros diseños, una vez que los productores están perfectamente definidos y funcionando, los podemos agrupar en sus respectivos bloques. Este ejemplo es exactamente el mismo que los dos anteriores, pero con los productores encapsulados
(09-UNIBUS-compartiendo-registro-III.ice)
Ahora se aprecia mucho mejor la estructura del circuito y el flujo de información
Esto es lo que se ha metido en el bloque productor 1:
El bloque productor 2 es similar, pero cambiando el valor que se produce al apretar el pulsador
Ejemplo 10: UNIBUS: Compartiendo un registro. Implementación 4
Para terminar esta sección, refinaremos nuestro ejemplo anterior un poco más: Crearemos bloques que incluyan el acceso al bus. De esta forma todo queda aún más compacto, y de un único vistazo sabemos lo que está ocurriendo: dos productores conectados en un bus que acceden a un registro
(10-UNIBUS-compartiendo-registro-III.ice)
Esto se hace típicamente en la última fase, cuando ya tenemos un diseño muy estable y que no requiera grandes cambios. En las etapas iniciales, cuando estamos diseñando, conviene no encapsular demasiado, porque todo está sometido a muchos cambios (tamaño del bus, salidas del productor, etc...)
Los nuevos bloques creados contienen el circuito del productor (que en este caso está encapuslado en el bloque productor1) y el bloque de acceso al bus. En esta figura vemos el productor 1:
Bus unidireccional con Feedback
Los controladores tienen señales de Feedback, que típicamente son:
- Busy: Indica el estado del controlador: Libre (ocioso) u Ocupando (en funcionamiento)
- Done: Tic que indica que ha finalizado la última operación
Estas señales las necesitamos si queremos acceder al controlador en bucle cerrado. Por ejemplo, antes de enviar un dato, podemos comprobar la señal de busy
para enviar el dato si ya está libre o esperar en caso de que esté ocupado. También podemos usar la señal de done
para transferir un grupo de datos a la máxima velocidad que permite el controlador
Las señales de Feedback se transmite por un bus en sentido contrario, que llamaremos Bus de Feedback
En realidad es un bus muy simple porque es de sólo lectura. Los productores NO escriben información en este bus, por lo que NO hay que tener elementos de acceso al bus
Como en Icestudio la información va de izquierda a derecha (siempre), este bus lo describiremos utilizando un par de etiquetas: una para crear el bus y otra para generarlo en la parte izquierda:
Este bus de Feedback lo recolocamos al lado del otro. La figura es igual, pero ahora ambos buses están juntos:
Ejemplo 11: Controlador falso (Fake controller)
Para hacer ejemplos en los siguientes apartados, primero vamos a construirnos un controlador falso, que simula a un periférico de escritura en los LEDs. La interfaz de este controlador es una entrada de datos de 4 bits (data
) y una señal de escritura (write
). Como salida tiene busy
, done
y el dato a mostrar en los LEDs
El productor que lo quiere usar debe introducir por su entrada el dato a escribir y enviar un tic por write
. El controlador espera un tiempo fijo (simulando los cálculos internos y operaciones que debe realizar) y finalmente muestra el valor en los LEDs
En este ejemplo se ha construido el controlador y se prueba con un circuito que escribe el valor 0xF
al apretar el pulsador SW1
. La señal de busy
se muestra por el LED7, y el controlador muestra el valor recibido en los LEDs 0 - 3
(11-fake-controller-test-1.ice)
El tiempo del controlador se ha establecido en 10 ciclos, muy rápido, para medirlo con el analizador lógico y entender mejor su temporización
Este es el escenario de pruebas, con la tarjeta Alhammbra-II conectada al analizador lógico. Al apretar el pulsador SW1
los LEDs 0 - 3 se encienden, y se hace la captura en el analizador
Ejemplo 12: Controlador falso. Implementacion II
Este ejemplo es similar al anterior, pero ahora hemos encapsulado el controlador false en el bloque fake controller, y hemos cambiado el tiempo de espera a 1 segundo. Ya no necesitamos el analizador lógico
(12-fake-controller-test-2.ice)
Al apretar SW1
veremos cómo el LED7 se enciende durante 1 segundo, indicando que el controlador está ocupado en sus cálculos internos. Transcurrido ese tiempo escribe el valor 0xF
en los LEDs, se apaga el LED7 y termina
Si apretamos nuevamente SW1
veremos cómo el LED7 se enciende nuevamente y se escribe otra vez el valor en los LEDs (pero como es el mismo que antes no notaremos ningún cambio)
En este vídeo de youtube se muestra su funcionamiento
Ejemplo 13: Secuencia en los LEDs
Este es un ejemplo de utilización de la señal done
del controlador falso para generar una secuencia de dos estados. Al apretar el pulsador SW1
se escriben los valores 0x3
y 0xC
en el controlador (mostrándose en los LEDs)
En este circuito sólo hay 1 productor, por lo que no es necesario ningún bus
(13-BUSF-secuencia-LEDs-test-1.ice)
En este vídeo de Youtube se muestra el funcionamiento del circuito
Ejemplo 14: Dos Secuencias en los LEDs
Si ahora queremos generar dos secuencias diferentes en los LEDs, activadas por los botones SW1
y SW2
, usamos un bus de 5 bits (4 bits de datos + 1 bit de write). La realimentación de la señal done
la hacemos a través de la etiqueta correspondiente
(14-BUSF-secuencia-LEDs-test-2.ice)
Las secuencias están controladas por una máquina de contador en cada productor. Su señal busy es la utilizada para el acceso al bus. Mientras la máquina 1 está activada, sus datos se inyectan en el bus
En este vídeo de Youtube se muestra el funcionamiento
Ejemplo 15: Dos secuencias en los LEDs. Implementación 2
Este ejemplo es exactamente el mismo que el ejemplo 14 (y ocupa los mismos recursos), pero se ha reestructurado. Los productores 1 y 2 se han metido en sus bloques correspondientes y las señales de feedbad (busy y done) se han agrupado en un bus de 2 bits, que sale del controlador
En otra parte del circuito, cuando se necesitan usar estas señales, se sacan directamente del bus de feedback
(15-BUSF-secuencia-LEDs-test-3.ice)
Este ejemplo es un paso intermedio hacia el bus con feecback final
Unificación en un BUS
El bus con los datos y el control se puede unificar con el bus de feedback para simplificar los bloques de los productores y la arquitectura en general
NOTA: Se puede unificar, pero no es obligatorio. En algunos circuitos puede interesar dejarlos por separado. Esto es decisión del diseñador
En caso de realizarse la unificación, situaremos las lineas de feedback en los bits de menor peso del Bus. A este bus unificado lo denominamos BUSF (Bus con Feedback)
El componente de acceso al bus ahora es ligeramente diferente porque los productores sólo acceden en escritura a los bits de datos y control, pero NO a los de Feedback (que sólo los escribe el controlador).
Para leer las señales de Feedback del bus, utilizamos un bloque Split, que obtiene el bus de datos + control por un lado, y las líneas de feedback por el otro
Para fijar las ideas, vamos a trabajar con el ejemplo 15, en el que tenemos 4 bits de datos, 1-bit de control y 2 bits de feedback. En total necesitamos un bus de 7-bits
Generación del BUSF
En los buses unidireccionales normales, el bus se crea en la izquierda, con un valor por defecto. En el caso del BUSF el bus lo crea el controlador. Se pone el valor por defecto en el bus de datos y de control, y se agrupan las señales de feedback. Todo ello se agrupa para formar el bus completo. Las agrupaciones se hacen usando bloques Join
En esta figura se muestra un ejemplo de construcción del BUSF de 7-bits
Esta construcción se hace en el controlador ampliado, que incluye el controlador normal junto con los elementos necesarios para la creación del BUSF
Lectura de los campos del BUSF
Para leer los campos del BUSF se utilizan bloques Split en cascada. En la primera etapa se extraen los sub-buses de feedback y de campos. En una segunda etapa se extraen los campos individuales
Acceso al BUSF
Los productores escriben en el BUSF pero SÓLO en el bus de campos, y deben dejar intactas las señales de Feedback. Por ello, el circito de acceso primero debe obtener el bus de campos (mediante un bloque Split), inyectar el valor en ese bus (dato + control) y volver a reconstruir el bus con las señales de feedback originales
Para el ejemplo que estamos construyendo, así es cómo se realiza:
Como esto hay que hacerlo para todos los productores que accedan al bus, conviene agruparlo en un nuevo bloque de acceso, que llamaremos BUSF-access. El bloque de este ejemplo es BUSF-access-7-5: Acceso a un BUSF de tamaño 7 bits, de los cuales 5bits se corresponden con el bus de campos
Controlador Ampliado
Por último, el controlador hay que envolverlo con el cableado necesario para extraer las señales de datos y control, y agrupar sus salidas para crear el BUSF (como hemos visto en apartados anteriores)
Para el ejemplo que estamos trabajando, la implementación del controlador ampliado es la mostrada en esta figura:
Y así es como queda el nuevo bloque:
Ahora ya resulta muy sencillo utilizarlo con el BUSF, y conectar diferentes productores, como veremos en le siguiente ejemplo
Ejemplo 16: Dos secuencias en los LEDs. Implementación 3
Así es como queda el ejemplo de las dos secuencias cuando usamos un BUSF unificado, con un controlador ampliado
(16-BUSF-secuencia-LEDs-test-4.ice)
La ventaja de este bus unificado es que ahora los productores los podemos ampliar para que se conecten directamente, simplificando todavía más los diseños, como vemos en el siguiente ejemplo
Ejemplo 17: Dos secuencias en los LEDs. Implementación 4
Este es el ejemplo final, en el que se usa un BUS único de 7-bits para conectar los dos productores al Controlador, y que ambos productores puedan leer las señales de Feedback del controlador a través del mismo bus
(17-BUSF-secuencia-LEDs-test-5.ice)
Bus Bidireccional
El caso más general de bus es cuando hay varias unidades que pueden intercambiar datos entre ellas. Todas ellas hacen de productores y de consumidores, pudiendo escribir en el bus y leer de él
En esta figura mostramos el esquema general, usando tres unidades de ejemplo, pero podría haber muchas más
Así, por ejemplo, la unidad 1 podría enviar información a la unidad 3 (U1->U3), o a la inversa (U3->U1), o bien la Unidad 2 enviar datos a la Unidad 1 (U2->U1), o la unidad 3 a la unidad 2 (U3->U2), etc...
Bus UNIDIRECCIONAL equivalente
El bus bidireccional es equivalente a uno UNIDIRECCIONAL, como el primero que vimos, pero con información adicional de lectura (por medio de una etiqueta). La clave está en dividir el bus en dos segmentos: uno donde las unidades hacen la escritura y otro donde hacen la lectura
Vamos a ir refinando el modelo general, incorporando detalles progresivamente, hasta llegar a obtener un bus equivalente Unidimensional
Las unidades son circuitos normales, que tienen patas diferentes para las entradas y las salidas. Así, por ejemplo, un registro de 2 bits tiene 2 bits de salida (salida q), y dos bits de entrada de datos (entrada d). En nuestro modelo esto lo representamos mediante flechas separadas. Todos los cables tienen una anchura de D bits, siendo D el tamaño del bus
En este tipo de buses, las señales eléctricas se propagan en ambas direcciones. Así, si la unidad 2 escribe en el bus, la señal se propaga hacia la derecha y hacia la izquierda, inundando el bus
Sin embargo, en las FPGAs y en el interior de los chips, los cables llevan la información sólo en una dirección. El dibujo anterior lo podemos reorganizar para que la información sólo fluja en una dirección. Basta con alargar el bus y curvarlo para pasar por la zona inferior de las unidades. Y desde ahí tirar los cables de lectura
Este esquema es totalmente equivalente al anterior, pero con cables que llevan la información en una dirección. Cada unidad puede transferir datos a cualquiera de las otras, con independencia de su situación en el bus, como se muestra en este ejemplo
Observamos que en el bus hay dos segmentos diferenciados. El segmento superior es donde se realizan las escrituras. Los cables de salida de las unidades se conectan en la parte superior. El segmento inferior es donde se conecta los cables de lectura del bus
Seguimos refinando el modelo. Ahora añadimos el valor por defecto del Bus, que típicamente es de 0, y además separamos los dos segmentos mediante una etiqueta, de manera que ahora ambos apuntan hacia la derecha (pero sigue siendo el mismo bus)
Este circuito sigue siendo totalmente equivalente a los anteriores: Todas las unidades se pueden comunicar con todas
El segmento del bus de lectura lo podemos sustituir por la etiqueta, en vez de dibujarlo como un bus. En los cables de escritura utilizamos un bloque de acceso al medio para la escritura de la información. Obtenemos el siguiente esquema equivalente, que incluye más detalles
Lo que tenemos ahora es en realidad un Bus unidireccional como los que hemos utilizado hasta ahora. El recurso compartido es la etiqueta BUS, que hace que la información llege a todos los consumidores (que son los propios productores). Pero a todos los efecto este bus general es similar al unidireccional. Los bloques de acceso al bus que usamos son los mismos que para los buses bidireccionales
Caso de estudio: Dos registros conectados en bus
Para entender bien los detalles, vamos a partir de un circuito en el que hay dos registros de 2 bits conectados mediante un bus general (unidireccional). El ejemplo se podría hacer con más registros, pero utilizaremos sólo 2 para simplicar la explicación. Llamaremos a cada unidad con el registro R1 y R2 respectivamente
El esquema general sería el siguiente:
Ahora lo convertimos en el bus unidireccional equivalente, y añadimos los detalles de todas las señales
Tenemos 4 señales de control o microórdenes:
l1
: Cargar el registro R1 con el valor disponible en el Bus (lectura)l2
: Cargar el registro R2 con el valor disponible en el Bus (lectura)oe1
: Volcar el registro R1 al Bus (escritura)oe2
: volcar el registro R2 al Bus (escritura)
Activando correctamente estas microórdenes se realizan las transferencia de información del registro R1 al R2 (R1->R2) o bien del R2 al R1 (R2->R1)
- Transferencia R1->R2: Hay que activar
oe1
yl2
. El resto de señales deben estar a cero (oe2=0, l1=0) - Transferencia R2->R1: Hay que activar
oe2
yl1
. El resto de señales deben estar a cero (oe1=0, l2=0)
La generación de las microórdenes la realiza un controlador especializado (lo que en las CPUs se conoce como Unidad de Control). Nosotros las generaremos manualmente, utilizando pulsadores
Ejemplo 18: Transferencias entre 2 registros
En este ejemplo materializamos las ideas presentadas en el apartado anterior. Conectamos ambos registros a través de un BUS BIDIRECCIONAL. Los registros R1 y R2 están inicializados con los valores 1 y 2 respectivamente. Su salidas, además, están conectadas a los LEDs para poder comprobar sus valores
Al apretar el pulsador SW1
se realiza la transferencia R2->R1 y al apretar SW2
la transferencia R1->R2
(18-BIDIR-Dos-regs-test-1.ice)
En este vídeo de Youtube se muestra su funcionamiento
Bus bidireccional UNIFICADO
El bus bidireccional en realidad está formado por dos partes: el segmento de escritura, que funciona igual que un bus unidireccional, y el segmento de lectura, para el que utilizamos la etiqueta del BUS.
Esto es suficiente para utilizar este bus en nuestro diseños. PERO en ocasiones, cuando la complejidad de los diseños es alta y queremos hacerlo más modular y legible, es posible crear un BUS UNIFICADO que incluya en paralelo tanto el segmento de escritura como el de lectura. Esto tiene la ventaja de que podemos crear módulos que se conectan directamente al BUS (sin tener que introducirles la etiqueta del bus como entrada)
Si nuestro bus es de D bits, cada segmento tiene D bits. Construimos un nuevo bus agrupando los dos segmentos, por lo que tendrá un tamaño de 2*D bits. Además, el segmento de escritura se une al de lectura tras llegar a la parte derecha (donde la etiqueta), ya que en ese punto se garantiza que todos las unidades han tenido la oportunidad de volcar su datos
Para realizar este conexionado, además del valor por defecto, necesitamos incluir un bloque especial para Crear el bus unificado. Los bloques de acceso también son especiales, ya que sólo escriben en el segmento de escritura (D bits). Este es el esquema:
En esta figura se muestran los detalles del bloque de creación del bus. El segmento de escritura va por los bits de mayor peso, y el de lectura por los bits de menor peso. El bloque primero separa los dos segmentos recibidos por la etiqueta. El que llega de escritura se conecta con el de lectura y se envía por el Bus. El de escritura se crea con el valor por defecto de 0
Este es el esquema del nuevo bloque de acceso al bus bidireccional. Recibe un bus de 2*D Bits, e inyecta el dato de D bits sólo en el segmento de escritura
Ejemplo 19: Transferencia entre dos registros. Implementación 2
Este es el mismo ejemplo que el 18: pero utilizando un bus unificado. Con este bus los registros ya se pueden encapsular en sus bloques correspondientes: R1
y R2
(19-BIDIR-Dos-regs-test-2.ice)
Esta es la implementación del bloque New Bus:
Y esta es la implementación del bloque R1
:
Y por último, esta es la implementación del bloque de acceso al bus bidireccional:
Conclusiones
Los buses nos permiten organizar y simplificar los diseños, para que sean más fáciles de entender. También nos permiten implementar diseños retro, que se basaban principalmente en tener un bus al que se conectan otros componentes (aunque las arquitecturas modernas no son "orientadas a buses")
Con estos buses ahora podemos implementar controladores de mayor nivel, haciendo circuitos que accedan a ellos a través del Bus. Esto nos será muy útil en los siguientes cuadernos técnicos para implementar controladores del LCD, del puerto serie, de acceso a memorias, etc...
Autor
- Juan González-Gómez (Obijuan)