Está en la página 1de 14

Tutorial básico de VHDL

Realizado por Claudio Alarcón Reyes


Universidad de Chile
Departamento de Ingeniería Eléctrica.
clalarco@ing.uchile.cl

El presente documento pretende ser una guía básica para el uso de VHDL. Se indicarán detalles
sobre el lenguaje mediante ejemplos, tanto en sintaxis como uso de las diversas herramientas
que posee VHDL.

El objetivo de este tutorial es que sea posible diseñar códigos de manera rápida utilizando lo
esencial del lenguaje. No se dará énfasis al uso de Testbenches asociados a simulación debido a
que las herramientas más ampliamente utilizadas (Max+Plus y Xilinx ISE) permiten realizar en
forma gráfica y mucho más simple este proceso.

Se irá explicado mediante ejemplos los conceptos mínimos de programación. Se debe revisar
los comentarios a los códigos pues poseen información útil a la hora de revisar sintaxis.

Se asume que el lector posee conocimientos sobre lógica combinacional y secuencial, y trabaja ya
sea con la plataforma Altera o Xilinx, o pretende usarlas.

Qué es VHDL?
Son las siglas de ‘VHSIC Hardware Design Language’, y VHSIC es acrónimo de ‘Very High Speed
Integrated Circuits’. Desde aquí se deduce que es un lenguaje para la creación de arquitecturas de
circuitos integrados. Se ha desarrollado de manera satisfactoria la rama digital de diseño, no así la
analógica (las siglas no dicen nada sobre si es diseño analógico o digital).

Otro lenguaje conocido para esta labor es Verilog HDL: la principal diferencia con el anterior es
que Verilog HDL está basado en la estructura de C, mientras que VHDL lo hace con Pascal. Sin
embargo, la lectura de VHDL es más simple debido a su acercamiento al lenguaje inglés.

La gran ventaja de este lenguaje es la abstracción de la realización física del circuito integrado
que se desea diseñar; esto quiere decir que lo que se debe considerar es el comportamiento final
del circuito. Por ejemplo, para realizar un circuito secuencial no se requiere saber cuál será la
implementación óptima con flip-flops, sino sólo realizar la implementación de la transición entre
estados.
Comenzando con el lenguaje
Se debe considerar que en este lenguaje no se trabaja en principio con enteros o decimales, sino
con niveles lógicos, aunque no solamente 1’s y 0’s. También existen otros como ‘Z’ (alta
impedancia), ‘X’ (don’t care) y ‘U’ (indefinido). Estos y otros estados pertenecen al tipo
STD_LOGIC, que es el estándar para trabajar con variables lógicas en VHDL.

Este lenguaje además no es sensible a mayúsculas (case sensitive), lo que permite que por
ejemplo MI_VARIABLE y My_VArIable sean el mismo elemento. A pesar de esto, los nombres no
deben comenzar con números, ni tener caracteres extraños (acentos, por ejemplo), ni doble guión
bajo (__).

Previamente, para evitar confusiones, se dará una pequeña reseña sobre la estructura de los
bloques en VHDL.

En pascal, la definición de las funciones es:

Función (parámetros)
Declaración de variables
Begin
Instrucciones
End función;

Esta estructura (con algunas modificaciones de acuerdo a cada situación) es heredada a VHDL.

Para comenzar, el ejemplo a continuación muestra la realización del siguiente circuito:

library IEEE;
use IEEE.STD_LOGIC_1164.all; -- librerias necesarias para usar std_logic

-- Para el nivel de este tutorial, la estructura siempre es similar


entity CIRCUITO is -- Entity señala la conexión con ‘el mundo externo’

-- PORT Indica las entradas y salidas del circuito


port (
-- Entradas
IN_1 : in STD_LOGIC;
IN_2 : in STD_LOGIC;
IN_3 : in STD_LOGIC;

-- Salida
OUT_1 : out STD_LOGIC -- El ultimo puerto no lleva ‘;’
);
end CIRCUITO;

-- ARCHITECTURE contiene la implementación física


architecture S of CIRCUITO is -- ‘S’ puede ser cualquier nombre válido

-- dentro de begin comienzan la conexión los dispositivos


begin
OUT_1 <= (IN_1 and IN_2) or IN_3; -- La asignación se realiza con <=
end S; -- Los operadores siguen la regla matemática
-- de prioridades.
Lo anterior también se puede escribir como (la definición de entity es idéntica):

architecture S of CIRCUITO is
-- definición de una señal
-- SIGNAL NOMBRE_SEÑAL : TIPO SEÑAL
signal SALIDA_AND : STD_LOGIC -- las señales (SIGNAL) se pueden considerar
-- como un ‘alambre’
begin
SALIDA_AND <= IN_1 and IN_2;
OUT_1 <= SALIDA_AND or IN_3;
end S;

Hay que tener en cuenta que todo lo que está dentro de begin se realiza en paralelo. Es decir, se
podría haber escrito:

begin
OUT_1 <= SALIDA_AND or IN_3;
SALIDA_AND <= IN_1 and IN_2;
end S;

y el resultado sería el mismo. Como las acciones dentro de una architecture se realizan en
paralelo, no se requiere verificar si las entradas han cambiado para actualizar los datos (en este
caso OUT_1), pues la arquitectura se puede considerar como el conjunto de conexiones físicas del
circuito. Junto con esto, las variables definidas como signal son bidireccionales, no así las
variables definidas en PORT (excepto las definidas como inout, de las que se habla más adelante).

En programas grandes se debe tener especial atención en la asignación doble y las asignaciones
circulares:

signal SALIDA_AND : in STD_LOGIC;


signal VAR_2 : in STD_LOGIC;

SALIDA_AND <= IN_1;


SALIDA_AND <= IN_2; -- doble asignación. Dos o más señales cambian a la misma

SALIDA_AND <= VAR_2;


VAR_2 <= SALIDA_AND; -- asignación circular. Ambas señales se asignan mutuamente
Hay que tener en cuenta que la asignación circular puede también producirse entre más de dos
variables.
Realizando circuitos secuenciales
En los circuitos secuenciales no todas las operaciones se realizan al mismo tiempo; por lo general
están sincronizadas a través de una señal de reloj y/o un enable, aunque esto no es necesario. En
VHDL esto se realiza en los bloques PROCESS. Dentro de estos bloques, las señales se ejecutan
secuencialmente.

En el siguiente ejemplo se implementará un Flip-flop JK controlado por flanco ascendente:

La ecuación que rige a este dispositivo es: Qk +1 = Qk J +Qk K , controlada mediante una señal de
reloj.

El código es el siguiente:

library IEEE;
use IEEE.STD_LOGIC_1164.all;

entity FF-JK is
port (
J : in STD_LOGIC;
K : in STD_LOGIC;
CLOCK : in STD_LOGIC;
Q : out STD_LOGIC;
NOT_Q : out STD_LOGIC;
);
end FF-JK;

architecture A of FF-JK is
signal Q_ANT, Q_NOW: STD_LOGIC; -- así se pueden definir dos o más variables del mismo tipo

begin
process (CLOCK)
-- entra a este bloque SOLO cuando ocurre un cambio en CLOCK.
-- Para indicar más de una entrada se escribe PROCESS (IN_1, IN_2, ... IN_N)
begin
if (CLOCK’EVENT and CLOCK=1) then -- condicional
-- clock’event indica si ha existido cambio en clock
-- (puede ser cualquier variable).

-- Estas dos instrucciones se ejecutan en orden.


Q_NOW <= (not(Q_ANT) and J) or (Q_ANT and not(K));
Q_ANT <= Q_NOW;
end if; -- debe llevar ‘;’ el END IF

end process;
Q <= Q_NOW;
NOT_Q <= NOT(Q_NOW);
end A;
Mientras no ocurra un cambio en el clock, las señales Q_ANT y Q_NOW no se modificarán, y por lo
tanto Q y NOT_Q permanecerán invariantes a pesar que J y K cambien, pues sólo cuando se
ejecuta el bloque ‘PROCESS (CLOCK)’ las señales cambian.
En este caso no es necesario revisar si se cumple CLOCK’EVENT, pues sólo CLOCK activa el proceso,
pero si hubiese más de un elemento de activación del proceso sería necesario revisarlo.

Es recomendable realizar fuera de los PROCESS todas las asignaciones posibles para una más rápida
implementación (por ejemplo, variables no requieran una asignación secuencial). Así también para
una mejor comprensión del código conviene no asignar los puertos de salida dentro de los
PROCESS, sino trabajar con señales y luego asignar las señales a las salidas. Esto además oculta las
transiciones intermedias de niveles lógicos (carreras) hacia la salida.
En términos generales se puede considerar que un bloque de PROCESS es una instrucción de la
arquitectura, por lo tanto, si existe más de un PROCESS, todos estos se ejecutarán en paralelo. Esto
se debe tener en cuenta en caso que más de un PROCESS sea activado por la misma entrada, pues
pueden modificar a las mismas señales al mismo tiempo.

Otros condicionales
Otras instrucciones como el ‘if’ para realizar condicionales son las siguientes:

IF (Uso en process)

If (condicion) then
Instrucciones secuenciales
Elsif (condicion2) then
Instrucciones secuenciales
Else
Instrucciones secuenciales
End if;

Case (uso en process)


Realiza diferentes instrucciones de acuerdo a lo que valga EXPRESION

case EXPRESION is
when VALOR_1 =>
-- instrucciones secuenciales

when VALOR_2 | VALOR_3 => -- VALOR_2 o VALOR_3


-- instrucciones secuenciales

when VALOR_4 to VALOR_N => -- desde VALOR_4 a VALOR_N


-- instrucciones secuenciales

when others => -- otro caso


-- instrucciones secuenciales
end case ;

With – Select (uso general)


Asigna valores a VAR_1 de acuerdo a cuánto valga EXPRESION

with EXPRESION select


VAR_1 <= VALOR_1 when OPCION_1, -- se separan por ‘,’
VALOR_2 when OPCION_2 | OPCION_3,
VALOR_3 when OPCION_4 to OPCION_5,
· · ·
VALOR_n when others; -- termina con ‘;’

Siempre se debe poner un valor para when others en un with – select pues valores válidos son
también por ejemplo alta impedancia y don’t care.
Uso de arreglos (vectores)
Elementos importantes son los vectores, pues se utilizan principalmente como buses de datos o
para manejar señales de más de un bit (como por ejemplo la dirección de una memoria ROM).

Los vectores se pueden crear, entre otras, de dos maneras:

-- bit más significativo en primera posición


Signal Un_vector : std_logic_vector (7 downto 0);

-- bit menos significativo en primera posición


Signal otro_vector : std_logic_vector (0 to 7);

Para el uso de cadenas de bits (por ejemplo un nibble o byte) se utiliza preferentemente la
primera forma, quedando el bit más significativo en la posición mayor. Si se asigna:

Un_vector <= "10000111"; -- entre comillas se asigna un valor a un vector


Otro_vector <= "10000111";

Los elementos de los vectores serán:


Bit 0 Bit 1 Bit 2 Bit 3 Bit 4 Bit 5 Bit 6 Bit 7
Un_vector 1 1 1 0 0 0 0 1
Otro_vector 1 0 0 0 0 1 1 1

Pues no se asigna por índice, sino por posición.

En el siguiente ejemplo se implementará una memoria ROM básica de 4x8, mostrando el uso de
los vectores:

library IEEE;
use IEEE.STD_LOGIC_1164.all;
entity ROM is
port ( ROM_ADDR : in STD_LOGIC_VECTOR (1 downto 0);
ROM_DATA : out STD_LOGIC_VECTOR (7 downto 0);
ROM_OE : in STD_LOGIC) -- Output enable. Si está deshabilitada
-- cambia a alta impedancia
end ROM;

architecture A of ROM is
begin
process (ROM_ADDR, ROM_OE) -- en process se puede indicar más
begin -- de una variable que cambie
if (ROM_OE=1) then
with ROM_ADDR select
ROM_DATA <= "00000000" when "00",
ROM_DATA <= "00000001" when "01",
ROM_DATA <= "00000011" when "10",
ROM_DATA <= "00000111" when OTHERS;
-- se debe poner others debido a las otras posibles entradas
-- (alta impedancia, don’t care, etc)
else
ROM_DATA <= "ZZZZZZZZ"; -- alta impedancia
end if;
end process;
end A;
Si por ejemplo el vector es muy largo, no es necesario escribir toda la cadena. Existen dos
maneras muy útiles de asignar valores a vectores:

- En hexadecimal, mediante la forma:


un_vector <= x"C8"; -- cada letra hexadecimal representa a 4 bits

- Inicializando a todos con el mismo valor


un_vector <= (others => 'Z') -- es comilla simple pues cada elemento vale 'Z'.

Hay otras formas, pero generalmente se trabaja con arreglos de tamaño potencias de 2, así que
con la asignación en hexadecimal es suficiente. La asignación hexadecimal señalada aquí sólo es
válida para la versión 93 de VHDL.

Más operaciones con vectores

Signal MI_SIGNAL : STD_LOGIC;


signal VECTOR1 : STD_SIGNAL_VECTOR (7 downto 0);
signal VECTOR2 : STD_SIGNAL_VECTOR (3 downto 0);
signal VECTOR3 : STD_SIGNAL_VECTOR (11 downto 0);

Acceder a un elemento del vector


VECTOR1(3) <= MI_SIGNAL

Selección de una sección del vector


VECTOR2 <= VECTOR1(5 downto 2);

Concatenación
VECTOR3 <= VECTOR1 & VECTOR2; -- deben coincidir los tamaños finales
Uso de componentes
Supongamos que queremos realizar un flip-flop T a partir de uno D. Las ecuaciones son:
T_DATA_IN = D_CLOCK;
D_DATA_IN = not D_Q;

Supongamos que ya teníamos una entidad llamada FF_D que es un flip-flop D, con entradas
DATA_IN y CLOCK, y salidas Q y NOT_Q, y se desea usar para realizar el Flip-flop T.

El código que realiza esto es el siguiente:

library IEEE;
use IEEE.STD_LOGIC_1164.all;
entity FF_T is
port (
DATA_IN : in STD_LOGIC;
Q : out STD_LOGIC;
NOT_Q : out STD_LOGIC
);
end FF_T;

architecture A of FF_T is
component FF_D -- Aquí se indica que se usará la entidad FF_D
port (
DATA_IN : in STD_LOGIC; -- Se indica su estructura.
CLOCK : in STD_LOGIC; -- Los nombres de los puertos
Q : out STD_LOGIC; -- deben coincidir con los originales
NOT_Q : out STD_LOGIC );
end component;

signal CXN_D_Q : STD_LOGIC; -- para conectar a Q y Data_in del flip-flop D

begin
FD : FF_D port map (
-- puerto de componente => puerto o señal de entidad
DATA_IN => CXN_D_Q, -- se separan por ‘,’
CLOCK => DATA_IN,
Q => CXN_D_Q, -- se conecta Q con DATA_IN
NOT_Q => NOT_Q -- el ultimo no lleva ‘,’
);
end A;

Se creó la señal CXN_D_Q pues no se pueden conectar dos puertos de componentes entre sí
directamente, ni tampoco dos puertos del mismo componente. Una entidad se puede entender
como un circuito, y un componente como un integrado. Para conectar por ejemplo dos pines del
integrado entre ellos, es necesario un cable (signal) que lo realice. Un puerto también puede
conectarse directamente a una salida del circuito (idealmente) sin necesidad de ningún cable.

Hay que tener en cuenta que para la correcta compilación de los componentes, el nombre de
archivo debe coincidir con el de la entidad. En Xilinx ISE se debe tener atención que los nombres
de los componentes sean además coincidentes en mayúsculas/minúsculas.
Si se desea realizar un circuito con dos o más componentes del mismo tipo, se deben crear los
port map necesarios. La definición de component es sólo una:

architecture A of VARIOS_FF is
component FF_D
port (
... );
end component;
...
begin
...
FD_A : FF_D port map (
... ) ;

FD_B : FF_D port map (


... ) ;
...
FD_N : FF_D port map (
... ) ;
end A ;
Uso de types
Los types se usan ya sea para crear una estructura o para realizar enumeración. Lo primero es útil
si se desea crear estructuras complejas, mientras que las enumeraciones son útiles para por
ejemplo definir estados en circuitos secuenciales.

Enumeración
La estructura es la siguiente:

type T_STATE is (ESTADO_1, ESTADO_2, ... ESTADO_N);

Por ejemplo, se desea crear un circuito secuencial que entregue un 1 si han ingresado dos unos
sucesivos. La tabla de transición de estados sería:

Estado Entrada = 0 Entrada = 1


A A,0 B,0
B A,0 B,1

entity MAQ_ESTADO is
port (
DATA_IN : in STD_LOGIC;
OUT : out STD_LOGIC;
CLOCK : in STD_LOGIC
);
end MAQ_ESTADO;
architecture A of MAQ_ESTADO is

type ESTADO is (ESTADO_A, ESTADO_B);


signal ESTADO_ACTUAL : ESTADO := ESTADO_A; -- Asignación inicial
signal OUT_SIGNAL : STD_LOGIC;
begin
process (CLOCK)
begin
if (CLOCK’EVENT and CLOCK=1) then -- flanco ascendente
if DATA_IN = 0 then
ESTADO_ACTUAL <= ESTADO_A;
OUT_SIGNAL <= 0;
else
if ESTADO_ACTUAL = ESTADO_B then
OUT_SIGNAL <= 1;
end if;
ESTADO_ACTUAL <= ESTADO_B;
end if;
end if;
end process;
OUT <= OUT_SIGNAL;
end A;

En el proceso de compilación VHDL asigna el tamaño y valores para implementar todos los
estados necesarios.

Estructuras
Las estructuras se dividen en dos conjuntos: arreglos (array) y registros (record). La principal
diferencia es que los arreglos permiten señales de un solo tipo, no así los registros.

Por ejemplo, para la implementación de una matriz de std_logic de 16x8 se puede crear un type
std_8bits y luego crear un arreglo de std_8bits:

type STD_8BITS is STD_LOGIC_VECTOR (7 downto 0);


type MEM_16X8 is array(7 downto 0) of STD_8BITS;
signal MI_MEMORIA : MEM_16X8;

Ahora, si se desea realizar un registro, por ejemplo, para describir un píxel en RGB, el código
sería:

architecture A of EJEMPLO is
type PIXEL is record
R : STD_LOGIC_VECTOR (7 downto 0); -- separados por ‘;’
G : STD_LOGIC_VECTOR (7 downto 0);
B : STD_LOGIC_VECTOR (7 downto 0); -- no necesariamente tienen que ser
end record; -- del mismo tipo

signal PIXEL1, PIXEL2, PIXEL3 : PIXEL;


begin
PIXEL1 <= ("10001000","10001000","00000000");
PIXEL2 <= PIXEL1;
PIXEL3 <= ("00000000", PIXEL1.G, PIXEL2.B); -- asignaciones válidas
end A;

Hay que considerar que para realizar cualquier asignación las variables deben ser del mismo tipo y
tamaño.

Operaciones matemáticas
VHDL posee librerías que poseen herramientas matemáticas como sumas, conversión, resta, etc.
Para la librería STD_LOGIC_ARITH el tipo de dato más usado es STD_INTEGER.

La función CONV_INTEGER permite convertir un elemento STD_LOGIC_VECTOR a STD_INTEGER.


Esta permite trabajar por ejemplo en el direccionamiento de una memoria RAM:

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_ARITH.all;

entity RAM is
port ( RAM_ADDR : in STD_LOGIC_VECTOR (1 downto 0);
RAM_DATA_IN : out STD_LOGIC_VECTOR (7 downto 0);
RAM_DATA_OUT : out STD_LOGIC_VECTOR (7 downto 0);
RAM_CE : in STD_LOGIC; -- chip enable
RAM_WE : in STD_LOGIC); -- write enable
end RAM;

architecture A of RAM is
type MEM_RAM is array(3 downto 0) of STD_LOGIC_VECTOR (7 downto 0);
signal MEMORIA :MEM_RAM;
signal RAM_DATA_SIGNAL : STD_LOGIC_VECTOR (7 downto 0);

begin
process (RAM_ADDR, RAM_OE, RAM_WE)
begin
if (RAM_CE=1) then
if (RAM_WE=1) then
-- se escribe en la RAM
MEM_RAM(conv_integer(RAM_ADDR)) <= RAM_DATA_IN;
end if;

-- se lee desde la RAM


RAM_DATA_SIGNAL <= MEM_RAM(conv_integer(RAM_ADDR));
else
RAM_DATA_SIGNAL <= "ZZZZZZZZ";

end if;
end process;
end A;

La tarjeta de desarrollo Xilinx posee una memoria RAM, por lo que es siempre conveniente realizar
la implementación de RAM’s y ROM’s en esta zona para no utilizar los recursos de la FPGA. Para
revisar cómo realizarlo (y ver implementaciones más específicas) se puede revisar la dirección:
http://toolbox.xilinx.com/docsan/xilinx6/books/data/docs/xst/xst0026_5.html. Además, la
plataforma UP2 de Altera mediante Max+Plus II permite trabajar con librerías internas llamadas
LPM, que están documentadas en la ayuda de Max+Plus II.

El siguiente ejemplo muestra un divisor de reloj por 4, mediante el uso de la librería


ieee.std_logic_unsigned.

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;

ENTITY div_clock IS
port (
in_clock : IN STD_LOGIC;
out_clock : OUT STD_LOGIC
);
END div_clock;

ARCHITECTURE rtl OF div_clock is


SIGNAL current_state : STD_LOGIC_VECTOR(1 downto 0);
BEGIN
out_clock <= current_state(1);
PROCESS (in_clock)
BEGIN
-- Entra solo en flanco positivo (divide por 2)
if (in_clock’event and in_clock = '1') then
current_state <= current_state + 1;
-- El carry de la suma pasa al siguiente bit más significativo
end if;
END PROCESS;
END rtl;
Uso de puertos bidireccionales
Estos puertos generalmente sólo son nombrados, aunque no se explica cómo usarlos. En esta
sección se pretenderá abordar este punto.

Los puertos bidireccionales no pueden trabajar como entrada y salida al mismo tiempo, sino que
debe alternarse su estado. Esto que parece simple no lo es, pues debe realizarse la asignación
alámbrica de entrada y salida, es decir, fuera de un process. Esto puede generar muchos
problemas de doble asignación, al imponerse involuntariamente dos valores como salida, o un
valor de entrada y otro de salida simultáneamente.

Para recibir información por el puerto, la salida debe asignarse como alta impedancia, pues este
estado permite que desde el otro terminal del puerto se impongan valores lógicos.

El ejemplo a continuación quizás enrede a quienes no conozcan el funcionamiento del 8031.

El puerto P0 del 8031 se utiliza para la adquisición de las instrucciones del programa a ejecutar:
en un instante envía por P0 y parte de P2, la dirección de memoria a leer de la ROM, y al
siguiente espera a que se envíe la instrucción. Para determinar si el puerto trabaja como entrada
o salida se utilizan las señales del microcontrolador ALE (address latch enable) y PSEN (program
strobe enable). El modo se determina el estado se detalla a continuación:

1
PSEN 0

1
ALE 0

P0 Dirección (salida) Alta Impedancia Instrucción (entrada)

Cuando ALE es uno, P0 impone el valor de la dirección de memoria a recibir. Cuando ALE es cero,
se mantiene el último valor de P0 en un registro de latch, lo que permite que P0 cambie a modo
de entrada.

Sin embargo, mientras PSEN no sea cero, la ROM no enviará la instrucción, y permanecerá en alta
impedancia. Cuando PSEN es cero, la ROM envía la instrucción a P0, que ya está en modo de
entrada de datos. Esto se realiza para que no existan inconsistencias de valores lógicos en las
transiciones.

Se desea simular la ROM y el latch (ambos asíncronos) para comunicarse con un 8031. La
descripción de los puertos y señales es la siguiente:

Microcontrolador:
uc_PSEN : PSEN
uc_ALE : ALE
uc_P0 (7 downto 0) : P0

ROM:
rom_addr (7 downto 0) : dirección de memoria a leer
rom_data (7 downto 0) : instrucción de la ROM

Señales:
addr_latch_signal (7 downto 0): recibe la dirección de memoria desde P0
data_signal (7 downto 0) : almacena la instrucción de la ROM para enviarse a P0
Hay que considerar que por simplicidad la dirección de memoria a utilizar será sólo de 8 bits, aunque el 8031
permite direcciones de hasta 14 bits (los otros bits se envían por P2). Además, la definición es vista desde la
ROM: P0 trabaja en modo de salida cuando la ROM le envía datos, pues salen hacia el microcontrolador; y es
entrada cuando se recibe la dirección de memoria en la ROM.

El código que realiza la comunicación de P0 con la ROM es el siguiente:

-- Conexión bidireccional de P0
-- uc_P0 como salida. Alta impedancia es para permitir entrada de información
-- sólo se envía la instrucción cuando uc_PSEN es cero
uc_p0 <=data_signal when (uc_PSEN='0') else (others =>'Z');

-- uc_P0 como entrada. Debe realizarse en un process para que


-- no cambie su valor si no se cumple condición de ALE.
-- Fuera de un process sería como una conexión 'alámbrica'
process
begin
if (uc_ALE='1') then
-- sólo cambia la dirección de la ROM cuando uc_ALE es 1
addr_latch_signal <= uc_P0;
end if;
end process;

Por simplicidad, en este ejemplo no se consideraron casos de falla (por ejemplo que PSEN sea
cero antes que ALE). Esto se puede considerar en las condiciones de entrada y salida de uc_P0, y
definir un estado intermedio, muy posiblemente asignando alta impedancia a uc_P0.

También podría gustarte