DESCRIPCION FUNCIONAL Comparador 2 bit mayor y menor library ieee; use ieee.std_logic_1164.all; entity carloscom is port (a,b: in bit_vector( 1 downto 0); c: out bit); end carloscom; architecture funcional of carloscom is begin process (a,b) begin if a = b then c <='1'; else c<='0'; end if; end process compara; end funcional; DESCRIPCION ESTRUCTURAL and 2 library ieee; use ieee.std_logic_1164.all; entity carlosand is port( a,b: in std_logic; f: out std_logic); end carlosand; architecture compuerta of carlosand is begin f <= '1' when (a ='1' and b = '1' ) else '0'; end compuerta; DESCRIPCION ESTRUCTURAL (MAPEO)falta error LOGICA COMBINACIONAL WHEN ELSE library ieee; use ieee.std_logic_1164.all ; entity tabla is port( a,b,c: in std_logic; f: out std_logic); end tabla; architecture ejemplo of tabla is begin f<= '1' when (a= '0' and b= '0' and c= '0' ) else '1' when (a= '0' and b= '1' and c= '1' ) else '1'when (a= '1' and b= '1' and c= '0' ) else '1' when (a= '1' and b= '1' and c= '1' ) else '0'; end ejemplo; CONCURRENTES ASIGNADAS A SEĂ&#x2018;ALES library ieee; use ieee.std_logic_1164.all ; entity concurrente is port ( A,B,C: in std_logic; X,Y,Z: out std_logic); end concurrente; architecture a_conc of concurrente is begin X <= (not A and not B and not C) or (not A and not B and C)
1
or (not A and B and C ) or (A and B and C) ; Y <= (not A and not B and C) or (A and not B and C) or (A and B and not C) ; Z <= (not A and not B and not C) OR (not A and B and not C) or (not A and B and C) ; end a_conc; WITH SELECT numerous primos library ieee; use ieee.std_logic_1164.all; entity seleccion is port ( X: in std_logic_vector(0 to 3); F: out std_logic) ; end seleccion; architecture a_selec of seleccion is begin with X select F <= '1' when "0001", '1' when "0010", '1' when "0011", '1' when "0101", '1' when "0111", '1' when "1011", '1' when "1101", '0' when others; end a_selec; library ieee; use ieee.std_logic_1164.all; entity comp is port ( A,B: in std_logic_vector(1 downto 0); Z: out std_logic_vector (1 downto 0)); end comp ; architecture a_comp of comp is begin process (A,B) begin if A = B then Z <= "11"; elsif A < B then Z <= "01"; else Z <= "10"; end if; end process; end a_comp;
BUFFER library ieee; use ieee.std_logic_1164.all ; entity tri_est is port( enable, entrada: in std_logic; salida: out std_logic); end tri_est; architecture arq_buffer of tri_est is begin process (enable, entrada) begin if enable = '0' then salida <= 'Z'; else
2
salida <= entrada; end if; end process ; end arq_buffer; mux de 4 bits library ieee; use ieee.std_logic_1164.all ; entity mux is port( a,b,c,d: in std_logic_vector(1 downto 0); s: in std_logic_vector (1 downto 0) ; Z: out std_logic_vector (1 downto 0) ); end mux; architecture arqmux4 of mux is begin with s select Z <= a when "00", b when "01", c when "10", d when others ; end arqmux4; sumador completo library ieee; use ieee.std_logic_1164.all; entity sum is port ( A,B,Cin: in std_logic; Suma, Cout: out std_logic); end sum; architecture a_sum of sum is begin Suma <= A xor B xor Cin; Cout <= (A and B) or ((A xor B) and Cin); end a_sum; library ieee; use ieee.std_logic_1164.all ; entity deco is port ( A: in std_logic_vector(3 downto 0); d: out std_logic_vector(6 downto 0)); end deco; architecture arqdeco of deco is begin process (A) begin case A is when "0000" => d <= "0000001"; when "0001" => d <= "1001111"; when "0010" => d <= "0010010"; when "0011" => d <= "0000110"; when "0100" => d <= "1001100"; when "0101" => d <= "0100100"; when "0110" => d <= "0100000"; when "0111" => d <= "0001110"; when "1000" => d <= "0000000"; when "1001" => d <= "0000100"; when others => d <= "1111111"; end case; end process; end arqdeco;
3
library ieee; use ieee.std_logic_1164.all ; entity codif is port ( a: in integer range 0 to 9; d: out std_logic_vector(3 downto 0)); end codif; architecture arqcodif of codif is begin process (a) begin If a = 0 then d <= "0000"; elsif a = 1 then d <= "0001"; elsif a = 2 then d <= "0010"; elsif a = 3 then d <= "0011"; elsif a = 4 then d <= "0100"; elsif a = 5 then d <= "0101"; elsif a = 6 then d <= "0110"; elsif a = 7 then d <= "0111"; elsif a = 8 then d <= "1000"; else d <= "1001"; end if; end process; end arqcodif; LOGICA SECUENCIAL library ieee; use ieee.std_logic_1164.all ; entity ffd is port ( D,clk: in std_logic; Q: out std_logic) ; end ffd; architecture arq_ffd of ffd is begin process (clk) begin if (clk'event and clk = '1') then Q<=D; end if ; end process ; end arq_ffd; library ieee; use ieee.std_logic_1164.all; entity reg is port ( D: in std_logic_vector(7 downto 0); clk: in std_logic; Q: out std_logic_vector(7 downto 0)); end reg ; architecture arqreg of reg is begin
4
process (clk) begin if (clk'event and clk = '1') then Q <= D; end if; end process; end arqreg; library ieee; use ieee.std_logic_1164.all; entity reg4 is port( D: in std_logic_vector(3 downto 0); CLK,CLR: in std_logic; Q,Qn: inout std_logic_vector(3 downto 0)); end reg4 ; architecture a_reg4 of reg4 is begin process (CLK,CLR) begin if (CLK'event and CLK ='1') then if (CLR = '1' )then Q <= D; Qn<= not Q; else Q <= "0000"; Qn <= "1111"; end if ; end if ; end process; end a_reg4; library ieee; use ieee.std_logic_1164.all ; use ieee.std_logic_unsigned.all; entity cont4 is port ( clk: in std_logic; Q: inout std_logic_vector(3 downto 0)); end cont4; architecture arqeont of cont4 is begin process (clk) begin If (clk'event and clk = '1') then Q <= Q + 1 ; end if; end process ; end arqeont; library ieee; use ieee.std_logic_1164.all ; use ieee.std_logic_unsigned.all; entity contador is port ( clk: in std_logic; UP:in std_logic; Q: inout std_logic_vector(3 downto 0)); end contador; architecture a_contador of contador is begin process (UP, clk) begin If (clk'event and clk = '1') then if (UP = '0') then Q <= Q + 1 ; else Q <= Q - 1 ;
5
end if ; end if ; end process ; end a_contador;
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity cont is port( P: in std_logic_vector(3 downto 0) ; clk,LOAD,ENP,RESET: in std_logic; Q: inout std_logic_vector(3 downto 0)); end cont; architecture arq_cont of cont is begin process (clk, RESET,LOAD, ENP) begin if (RESET = '1') then Q <= "0000"; elsif (clk'event and clk = '1') then if (LOAD = '0' and ENP = '-') then Q <= P; elsif (LOAD = '-' and ENP = '0') then Q <= Q; elsif (LOAD = '1' and ENP = '1') then Q <= Q + 1; end if; end if; end process; end arq_cont; library ieee; use ieee.std_logic_1164 . all; entity diagrama is port( clk,x: in std_logic; z: out std_logic) ; end diagrama; architecture arq_diagrama of diagrama is type estados is (dO, di, d2, d3); signal edo_presente, edo_futuro: estados; begin procesol: process (edo_presente, x) begin case edo__presente is when dO => z <= '0'; if x =' 1' then edo_futuro <= di; else edo_futuro <= dO; end if; when di => z <='0'; if x='l' then edo_futuro <= d2; else
6
edo_futuro <= di; end if; when d2 => z <='0'; if x='l' then edo_futuro <= d3; else edo_futuro <= dO; end if; when d3 => if x='l' then edo_futuro <= dO; z <='1'; else edo_futuro <= d3; z <= '0'; end if; end case; end process procesol; proceso2: process(clk) begin if (clk'event and clk='l') then edo_presente <= edo_futuro; end if; end process proceso2; end arq_diagrama ; //////////////////////////////////////////////////////////////// Mux de 4 entradas 2 selectore Library ieee; use ieee.std_logic_1164.all; entity mux4 is port (E: in std_logic_vector(3 DOWNTO 0); F: out std_logic; S: in std_logic_vector(1 DOWNTO 0)); end mux4; architecture rtl of mux4 is begin with S select F<= E(0) when "00", E(1) when "01", E(2) when "10", E(3) when others; end rtl; deco de 3 entradas a 8 library ieee; use ieee.std_logic_1164.all; entity deco3 is port (I: in std_logic_vector(2 DOWNTO 0); S: out std_logic_vector(7 DOWNTO 0); ENABLE: in std_logic); end deco3; architecture rtl of deco3 is signal sAUX: std_logic_vector(7 DOWNTO 0); begin with I select sAUX<= "00000001" when "000", "00000010" when "001", "00000100" when "010", "00001000" when "011", "00010000" when "100",
7
"00100000" when "101", "01000000" when "110", "10000000" when others; S<= sAUX when ENABLE='1' ELSE "00000000"; end rtl; comparador de 4 bits mayor igual menor LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; ENTITY comp4 IS PORT (BCD1, BCD2: IN std_logic_vector(3 DOWNTO 0); Igual, Mayor, Menor: OUT std_logic); END comp4; ARCHITECTURE RTL OF comp4 IS BEGIN PROCESS(BCD1, BCD2) BEGIN IF (BCD1 = BCD2) THEN Igual <= '1'; ELSE Igual <= '0'; END IF; IF (BCD1 > BCD2) THEN Mayor <= '1'; ELSE Mayor <= '0'; END IF; IF (BCD1 < BCD2) THEN Menor <= '1'; ELSE Menor <= '0'; END IF; END PROCESS ; END RTL;
Comparador de 4 bits lo mismo LIBRARY ieee; use ieee.std_logic_1164.all; USE ieee.std_logic_unsigned.all ; ENTITY comparador IS PORT ( A,B: IN STD_LOGIC_VECTOR(3 DOWNTO 0); igual,mayor,menor: OUT STD_LOGIC); END comparador; ARCHITECTURE rtl OF comparador IS BEGIN igual <= '1' WHEN A = B ELSE '0'; mayor <= '1' WHEN A > B ELSE '0'; menor <= '1' WHEN A < B ELSE '0'; END rtl; Sumador completo con acarreo LIBRARY ieee ; USE ieee.std_logic_1164.ALL ; USE ieee.std_logic_unsigned.ALL ; ENTITY suma4 IS PORT (A,B: IN std_logic_vector (3 downto 0); ci: in std_logic; S: out std_logic_vector (3 downto 0); co: out std_logic); END suma4 ; ARCHITECTURE entera OF suma4 IS signal suma: std_logic_vector(4 downto 0); BEGIN suma <= ('0'& A) + B + ci ; S <= suma(3 downto 0); co <= suma(4); END entera ;
8
Contador 4 bits flanco bajada osea comienza en 0 ENTITY contador4 IS PORT( clock, reset: IN BIT; salida : OUT INTEGER RANGE 0 TO 15); END contador4; ARCHITECTURE rtl OF contador4 IS BEGIN PROCESS (clock, reset) VARIABLE cuenta :INTEGER RANGE 0 TO 15; BEGIN IF (reset='1') THEN cuenta:=0; ELSIF (clock'EVENT AND clock='0') THEN cuenta:=cuenta+1; END IF; salida <= cuenta; END PROCESS; END rtl; Contador ascendente descendente 4 bits ENTITY Contador_asc_desc IS PORT( clock, enable, asc_des : IN BIT; salida : OUT INTEGER RANGE 0 TO 15); END Contador_asc_desc; ARCHITECTURE rtl OF Contador_asc_desc IS BEGIN PROCESS (clock) VARIABLE cuenta :INTEGER RANGE 0 TO 15; BEGIN IF (clock'EVENT AND clock='0') THEN IF (enable = '1' AND asc_des = '1') THEN cuenta:=cuenta+1; ELSIF (enable = '1' AND asc_des = '0') THEN cuenta:=cuenta-1; END IF; END IF; salida <= cuenta; END PROCESS; END rtl; Contador de 8bits library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity contador8bits is port ( cout :out std_logic_vector (7 downto 0); enable :in std_logic; clk :in std_logic; reset :in std_logic ); end contador8bits; architecture rtl of contador8bits is signal count :std_logic_vector (7 downto 0); begin process (clk, reset) begin if (reset = '1') then count <= (others=>'0'); elsif (rising_edge(clk)) then if (enable = '1') then count <= count + 1;
9
end if; end if; end process; cout <= count; end rtl; Algunos tipos básicos predefinidos • INTEGER: tipo entero – usado como valor índice en lazos, constantes o valores genéricos • BOOLEAN: tipo lógico – Puede tomar como valores ‘TRUE’ o ‘FALSE’ • ENUMERATED: Enumeración – Conjunto de valores definido por el usuario – Por ejemplo: TYPE estados IS (inicio, lento, rapido) Asignación de señales en buses SIGNAL tmp: STD_LOGIC_VECTOR(7 downto 0); – Vamos a definir una señal de 8 bits para trabajar con ella: – Asignación de un valor binario: tmp <= "10100011"; – Asignación de un valor en hexadecimal: tmp <= x"A3"; tmp(7) <= '1'; tmp(7 downto 4) <= "1010"; Usaremos estos objetos como ejemplo: signal stdv: std_logic_vector(7 downto 0); variable uns: unsigned(7 downto 0); variable sig: signed(7 downto 0); variable entero: Integer – Conversión de signed y unsigned a std_logic_vector: stdv<=std_logic_vector(uns); stdv<=std_logic_vector(sig); – Conversión de std_logic_vector a signed y unsigned: uns := unsigned(stdv); sig := signed(stdv); --Conversión de signed y unsigned a Integer: entero := to_integer(sig); entero := to_integer(uns); – Conversión de Integer a signed y unsigned: uns := to_unsigned(entero,8); sig := to_signed(entero,8); – Conversión de std_logic_vector a Integer y vice-versa stdv <= std_logic_vector(to_unsigned(entero,8)); entero := to_integer(unsigned(stdv)); signal a: std_logic_vector( 3 downto 0); signal b: std_logic_vector( 3 downto 0); signal c: std_logic_vector( 7 downto 0); a <= "0011"; b <= "1010"; c <= a & b; -- c ="00111010" signal a: unsigned( 3 downto 0); signal b: unsigned( 3 downto 0); a <= "0011"; b <= shift_left(a,1); -- b ="0110" b <= shift_right(a,1); -- b ="0001"
10
alu : process (op1, op2, cmd) is begin case cmd is when "00" => res <= op1 + op2; when "01" => res <= op1 â&#x20AC;&#x201C; op2; when "10" => res <= op1 and op2; when "11" => res <= op1 or op2; when others => res <= "XXXXXXXX"; end case; end process alu; end architecture uam; architecture uam of ejemplo is begin process(A,B,C) variable S : std_logic; begin S := A and B; if C='1' then Q <= '1'; else Q <= S; end if; end process; end uam;
11
architecture uam of coder is begin s <= "111" when a(7)='1' else "110" when a(6)='1' else "101" when a(5)='1' else "100" when a(4)='1' else "011" when a(3)='1' else "010" when a(2)='1' else "001" when a(1)='1' else "000"; end architecture uam; architecture uam of decod is begin with a sel s <= "00000001" when "000", "00000010" when "001", "00000100" when "010", "00001000" when "011", "00010000" when "100", "00100000" when "101", "01000000" when "110", "10000000" when others; end architecture uam; Para vectores de bits: "01111" binario O"17" octal X"F" hexadecimal Para enteros y reales: 2#1100# binario 12 decimal 16#C# hexadecimal
Tipos de datos Como en cualquier lenguaje de programación existen varios tipos de datos, en VHDL se pueden diferenciar dos: escalares y compuestos. Tipos escalares Son tipos simples que contienen algún tipo de magnitud. • Enteros: Son datos con un valor numérico entero. La forma de definirlos es con la palabra RANGE. Realmente se dice que un número está en un límite establecido. TYPE byte IS RANGE 0 TO 255; • Físicos: Se trata de datos que corresponden con magnitudes físicas, que tienen un valor y unas unidades. TYPE longitud IS RANGE 0 TO 1.0e9 UNITS um; mm=1000 um; m=1000 mm; in=25.4 mm; END UNITS; • Reales o coma flotante: Se definen igual que los enteros con la diferencia que los límites son números reales. TYPE nivel IS RANGE 0.0 TO 5.0
12
•
Enumerados: Son datos que puede tomar siempre que se encuentre en una lista o conjunto finito. Es idéntico a las enumeraciones en C (enum). TYPE color IS (ROJO, VERDE, AMARILLO);
Tipos compuestos Son tipos de datos compuestos por los que se han visto anteriormente. • Matrices: Se trata de un conjunto de elementos del mismo tipo, accesibles mediante un índice. Los hay de dos tipos: monodimensionales o multidimensionales. TYPE word IS ARRAY (31 DOWNTO 0) OF bit; TYPE tabla IS ARRAY (1 TO 4, 1 TO 4) OF real; En este punto es necesario explicar la palabra reservada OTHERS, donde es posible asignar un determinado valor a todos los elementos de la matriz. word <= (OTHERS => '0'); -- Asigna '0' en todas las posiciones Las palabras reservadas TO y DOWNTO sirven para indicar los índices de una matriz. El primero indica un rango ascendente (de x a y), mientras que el segundo es descendente (desde x hasta y). -- word1 y word2 son elementos idénticos TYPE word1 IS ARRAY (31 DOWNTO 0) OF bit; TYPE word2 IS ARRAY (0 TO 31) OF bit; Dependiendo de la opción elegida el bit más significativo corresponderá al primer bit (0) o al último (31). También es posible obtener un trozo de una matriz para poder realizar operaciones con ella. TYPE word IS ARRAY (31 DOWNTO 0) OF bit; TYPE subword IS ARRAY (7 DOWNTO 0) OF bit; ... subword <= word(31 DOWNTO 24); Además, es posible asignar a una matriz una lista separada por comas, de forma que el primer elemento de la lista corresponde al primero de la matriz. semaforo <= (apagado, apagado, encendido); luces <= (apagado, verde, azul, ..., amarillo); • Registros: Es equivalente al tipo record de otros lenguajes. TYPE trabajador IS RECORD nombre : string; edad : integer; END RECORD; Para acceder a algún atributo del registro se utilizará el punto. trabajadorA.nombre="Juan"
Subtipos de datos En VHDL se pueden definir subtipos, los cuales corresponden a tipos ya existentes. Se pueden diferenciar dos tipos dependiendo de las restricciones que se apliquen. • Restricciones de un tipo escalar a un rango: SUBTYPE indice IS integer RANGE 0 TO 7; SUBTYPE digitos IS character RANGE '0' TO '9'; • Restricciones del rango de una matriz: SUBTYPE id IS string(0 TO 15); SUBTYPE dir IS bit_vector(31 DOWNTO 0); La ventaja de utilizar subtipos se basa a la hora de sintetizar un circuito, ya que si se utiliza el propio tipo, como puede ser el integer, se interpretará como un bus de 32 líneas, pero realmente sólo harán falta muchas menos.
Conversión de tipo En ocasiones puede ser necesario convertir unos tipos a otros, esta operación es conocida como casting. Algunas de las conversiones son automáticas, como puede ser el paso de entero a real, otras conversiones deben realizarse de forma explícita, indicando el nombre del tipo al que se quiere pasar seguido del valor entre paréntesis. real(15); integer(3.5); En muchos diseños es necesario realizar conversiones entre bits y enteros. A continuación se muestran varias funciones de conversión entre ambos tipos. conv_integer(std_logic_vector); -- Conversión de vector a entero conv_std_logic_vector(integer, numero_bits); -- Conversión de entero a vector de numero_bits de tamaño
13
Constantes, señales y variables En VHDL existen tres tipos de elementos: señales, constantes y variables. Estas dos últimas tienen un significado similar a cualquier otro lenguaje de programación. Todos estos elementos son diferentes. Las variables sólo tienen sentido dentro de los procesos o subprogramas, mientras que las señales pueden ser declaradas en arquitecturas, paquetes o bloques concurrentes. Las constantes pueden ser declaradas en los mismos sitios que las variables y señales.
Constantes Es un elemento que se inicializa con un valor determinado, el cual no puede ser modificado, es decir siempre conserva el mismo valor. Esto se realiza con la palabra reservada CONSTANT. CONSTANT e : real := 2.71828; CONSTANT retraso : time := 10 ns; También es posible no asociar un valor a una constante, siempre que el valor sea declarado en otro sitio. CONSTANT max : natural;
Variables Es lo mismo que una constante, pero con la diferencia que puede ser modificada en cualquier instante, aunque también es posible inicializarlas. La palabra reservada VARIABLE es la que permite declarar variables. VARIABLE contador : natural := 0; VARIABLE aux : bit_vector(31 DOWNTO 0); Es posible, dado un elemento cambiarle el nombre o ponerle nombre a una parte mediante la palabra reservada ALIAS. VARIABLE instruccion : bit_vector(31 DOWNTO 0); ALIAS cod_op : bit_vector(7 DOWNTO 0) IS instruccion(31 DOWNTO 24); }
Señales Las señales se declaran con la palabra reservada SIGNAL, a diferencia con las anteriores este tipo de elementos pueden ser de varios tipos: normal, register o bus. Es posible asignarles un valor inicial. SIGNAL sel : bit := '0'; SIGNAL datos : bit_vector(7 DOWNTO 0);
Atributos Los elementos como señales y variables pueden tener atributos, éstos se indican a continuación del nombre, separados con una comilla simple "'" y pueden incluir información adicional de algunos objetos desarrollados en VHDL, que servirán a las herramientas de diseño para obtener información a la hora de realizar una síntesis. Existen muchos atributos, como LEFT, RIGHT, LOW, HIGH, RANGE, LENGTH... Pero el atributo más usado es EVENT, que indica si una señal ha cambiado de valor. Por ejemplo la siguiente sentencia captura un flanco de subida de una señal (clk). .... if clk'event and clk = '1' then ....
Definición de atributos Un atributo definido por el diseñador siempre devolverá un valor constante. En primer lugar se debe declarar el atributo, mediante la palabra reservada ATTRIBUTE, indicando el tipo de elemento que se devuelve, seguidos el valor que se retornará. La sintaxís para definir atributos sería la siguiente. ATTRIBUTE nombre : tipo ATTRIBUTE nombre OF id_elemento : clase IS valor Donde nombre es el identificador del atributo, id_elemento corresponde al identificador de un elemento del lenguaje definido previamente (señal, variable, etc.), la clase es el tipo de elemento al que se le va añadir dicho atributo, es decir si es señal, constante, etc. y el valor será lo que devuelva al preguntar por dicho atributo. Un ejemplo de todo esto puede ser el siguiente. SIGNAL control : std_logic; ATTRIBUTE min : integer; ATTRIBUTE min OF control : SIGNAL IS 4; .... IF control'min > 20 THEN
Operadores Los operadores que proporciona el lenguaje son:
14
•
Lógicos: Actúan sobre los tipos bit, bit_vector y boolean. En el caso de utilizar este tipo de operadores en un vector, la operación se realizará bit a bit. Operadores: AND, OR, NAND, NOR, XOR, XNOR y NOT. • Aritméticos: • + (suma o signo positivo): Sirve para indicar una suma entre dos números. También puede actuar como símbolo si se sitúa delante de una expresión. • - (resta o signo negativo): Sirve para indicar la resta entre dos números. Si va delante de una expresión modifica el signo de la expresión. • * (multiplicación): Multiplica dos números de cualquier tipo. • / (división): Divide dos números de cualquier tipo. • ** (exponencial): Eleva un número a una potencia. El número de la izquierda puede ser entero y real, pero el de la derecha sólo puede ser entero. Ejemplo: 4**2 sería 4². • ABS() (valor absoluto): Devuelve el valor absoluto de su argumento. • MOD (módulo): Calcula el módulo de dos números. • REM (resto): Calcula el resto de la división. • Relacionales: Siempre devuelven un valor booleano (true o false). • ==, /= (igualdad): El primero devuelve verdadero si los operando son iguales y falso en caso contrario. El segundo indica desigualdad, funcionando al revés que el anterior. • >, >=, <, <= (menor mayor): Poseen el significado habitual (mayor que, mayor o igual que, menor que, menor o igual que, respectivamente). La diferencia con los anteriores reside en su uso, en este caso los tipos de datos que pueden manejar son siempre de tipo escalar o matrices. • Desplazamientos: (incluidas en la revisión de 1993) • SLL (Shift Left Logic) y SRL (Shift Right Logic), desplazamiento lógico a la izquierda y desplazamiento lógico a la derecha, respectivamente, rellenando de ceros los huecos. • SLA (Shift Left Arithmetic) y SRA (Shift Right Arithmetic), desplazamiento aritmético a la izquierda y derecha respectivamente. • ROL (ROtate Left) y ROR (ROtate Right), rotación a la izquierda y a la derecha respectivamente. En este caso los huecos son ocupados por los bits que van quedando fuera.
Descripción de flujo de datos A la hora de plantearse crear un programa en VHDL no hay que pensar como si fuera un programa típico para ordenador. No hay que olvidar que en VHDL hay que describir un hardware, algo que no se hace en un programa para ordenador. Un circuito electrónico puede tener muchos elementos que estén ejecutando acciones a la vez, por ejemplo en un circuito puede tener una entrada que se aplique a dos puertas lógicas y de cada una obtener una salida, en este caso tendría dos caminos en los que se ejecutarían acciones (las puertas lógicas) de forma paralela. Esto es lo que se llama concurrencia. VHDL es un lenguaje concurrente, como consecuencia no se seguirá el orden en que están escritas las instrucciones a la hora de ejecutar el código. De hecho, si hay dos instrucciones, no tiene porqué ejecutarse una antes que otra, pueden ejecutarse a la vez.
Sentencias Concurrente La instrucción básica de la ejecución concurrente es la asignación entre señales a través del símbolo <=. Para facilitar la asignación de las señales VHDL incluye elementos de alto nivel como son instrucciones condicionales, de selección, etc, que se verán a continuación. WHEN ... ELSE Un posible ejemplo de este tipo de sentencias podría ser la siguiente. s <= "00" WHEN a = b ELSE "01" WHEN a > b ELSE "11";
WITH ... SELECT ... THEN Es similar a las sentencias CASE o SWITCH de C. La asignación se hace según el contenido de un objeto o resultado de cierta expresión. WITH <señal1> SELECT <señal2> <= <asignación1> WHEN <estado_señal1>,
15
<asignación2> WHEN <estado_señal2>, ... <asignaciónN> WHEN OTHERS; Un ejemplo de esta sentencia es la siguiente. WITH estado SELECT semaforo <= "rojo" "verde" "amarillo" "roto"
WHEN WHEN WHEN WHEN
"01", "10", "11", OTHERS;
La cláusula WHEN OTHERS especifica todos los demás valores que no han sido contemplados. También es posible utilizar la opción que se contempló en el caso anterior (UNAFFECTED).
BLOCK En ocasiones interesa agrupar un conjunto de sentencias en bloques. Estos bloques permiten dividir el sistema en módulos, estos módulos pueden estar compuestos de otros módulos. La estructura general es la siguiente. block_id; BLOCK(expresión de guardia) cabecera declaraciones BEGIN sentencias concurrentes END BLOCK block_id; El nombre del bloque es opcional (block_id), al igual que la expresión de guardia. Un ejemplo de esto podría ser el siguiente. latch: BLOCK(clk='1') BEGIN q <= GUARDED d; END BLOCK latch;
Descripción de comportamiento Como la programación concurrente no siempre es la mejor forma de describir ideas, VHDL incorpora la programación serie, la cual se define en bloques indicados con la sentencia PROCESS. En un mismo diseño puede haber varios bloques de este tipo, cada uno de estos bloques corresponderá a una instrucción concurrente. Es decir, internamente la ejecución de las instrucciones de los PROCESS es serie, pero entre los bloques es concurrente. A continuación se verán la estructuras más comunes de la ejecución serie y sus características.
PROCESS Un PROCESS, como se ha dicho antes, es una sentencia concurrente en el sentido de que todos los PROCESS y todas las demás sentencias concurrentes se ejecutarán sin un orden establecido. No obstante las sentencias que hay dentro del PROCESS se ejecutan de forma secuencial. Por lo tanto se puede decir que una estructura secuencial va en el interior de un PROCESS. La estructura genérica de esta sentencia es: PROCESS [lista de sensibilidad] [declaración de variables] BEGIN [sentencias secuenciales] END PROCESS; La lista de sensibilidad es una serie de señales que, al cambiar de valor, hacen que se ejecute el PROCESS. Un ejemplo sería: PROCESS(señal1, señal2) ... El PROCESS anterior sólo se ejecutará cuando señal1 o señal2 cambien de valor. Variables y Señales[editar] Hay que distinguir las señales y las variables, las señales se declaran entre ARCHITECTURE y su correspondiente BEGIN mientras que las variables se declaran entre PROCESS y su BEGIN. Dentro de un PROCESS pueden usarse ambas, pero hay una diferencia importante entre ellas: las señales sólo se actualizan al
16
terminar el proceso en el que se usan, mientras que las variables se actualizan instantáneamente, es decir, su valor cambia en el momento de la asignación. Unos ejemplos son: ENTITY ejemplo PORT (c: IN std_logic; d: OUT std_logic); END ENTITY; ARCHITECTURE ejemplo_arch OF ejemplo IS SIGNAL a,b: std_logic; BEGIN PROCESS(c) VARIABLE z: std_logic; BEGIN a<= c and b; --asignación de señales: después seguirá valiendo lo mismo, sólo se actualiza al acabar el z:= a or c; --asignación de variables: en el línea z valdrá a or c (el valor que tenía a cuando empezó END PROCESS; END ARCHITECTURE;
de ejecutarse esta línea a PROCESS momento de ejecutarse esta el PROCESS)
Sentencias secuenciales[ IF ... THEN ... ELSE[ Permite la ejecución de un bloque de código dependiendo de una o varias condiciones. IF <condición1> THEN [sentencias 1] ELSIF <condición2> THEN [sentencias 2] ELSE [sentencias N] END IF; Un ejemplo es: IF (reloj='1' AND enable='1') THEN salida<=entrada; ELSIF (enable='1') THEN salida<=tmp; ELSE salida<='0'; END IF;
CASE[ Es parecido al anterior porque también ejecuta un bloque de código condicionalmente, pero en esta ocasión se evalúa una expresión en vez de una condición. Se debe recordar que se deben tener en cuenta todos los casos, es decir, incluir como última opción la sentencia WHEN OTHERS. CASE <expresión> IS WHEN <valor1> => [sentencias1] WHEN <valor2> => [sentencias2] WHEN <rango de valores> => [sentenciasN] WHEN OTHERS => [sentenciasM] END CASE; Un ejemplo es: CASE a IS WHEN 0 WHEN 1 to 50 WHEN 99 to 51 WHEN OTHERS END CASE;
=> => => =>
B:=0; B:=1; B:=2; B:=3;
17
Entity multiplexor is port( a,b,c: in bit_vector(3 downto 0); control:in bit_vector(1 downto 0); enable: in bit; d: out bit_vector(3 downto 0); ); end multiplexor;
En este caso, las entradas son a, b, y c, la seĂąal de control es control, la seĂąal de habilitaciĂłn es enable, y la salida es d. Las entradas y la salida siguen siendo de 4 bits de ancho, y al haber mas de dos, control debe aumentar su anchura a dos bits.
control
enable
d
XX
H
HHHH
LL
L
a
LH
L
b
HL
L
c
HH
L
HHHH
entity multi is port( a, b, c :in bit_vector(3 downto 0); enable :in bit; control :in bit_vector(1 downto 0); d :out bit_vector(3 downto 0) ); end multi; architecture archmul of multi is begin process (a, b, c, control, enable) begin if enable='1' then d<="1111"; elsif enable='0' then case control is when "00" => d <= a; when "01" => d <= b; when "10" => d <= c; when others => d <= "1111"; end case; end if; end process; end archmul; entity coder is port( dentro: in bit_vector(3 downto 0); fuera : out bit_vector(1 downto 0); error : out bit ); end coder; architecture archicoder of coder is begin process (dentro) begin case dentro is when "0001" => fuera <= "00"; when "0010" => fuera <= "01"; when "0100" => fuera <= "10"; when "1000" => fuera <= "11"; when others => error <= '1'; end case;
18
end process; end archicoder; codificador binario 8 a 3 con prioridad. dentro
fuera
EO
HXXXXXXX LHXXXXXX LLHXXXXX LLLHXXXX LLLLHXXX LLLLLHXX LLLLLLHX LLLLLLLH
HHH HHL HLH HLL LHH LHL LLH LLL
L L L L L L L L
LLLLLLLL
LLL
H
library ieee; use ieee.std_logic_1164.all; use work.std_arith.all; entity coder is port( dentro: in std_logic_vector(7 downto 0); fuera : out std_logic_vector(2 downto 0); eo : out bit ); end coder; architecture archicoder of coder is begin process (dentro) begin if std_match(dentro,"1-------") then fuera<="111"; eo<='0'; end if; if std_match(dentro,"01------") then fuera<="110"; eo<='0'; end if; if std_match(dentro,"001-----") then fuera<="101"; eo<='0'; end if; if std_match(dentro,"0001----") then fuera<="100"; eo<='0'; end if; if std_match(dentro,"00001---") then fuera<="011"; eo<='0'; end if; if std_match(dentro,"000001--") then fuera<="010"; eo<='0'; end if; if std_match(dentro,"0000001-") then fuera<="001"; eo<='0'; end if; if std_match(dentro,"00000001") then fuera<="000"; eo<='0'; end if; if std_match(dentro,"00000000") then fuera<="000"; eo<='1'; end if; end process; end archicoder; Decodificador de BCD a siete segmentos Entrada (BCD)
Salida (LED)
LLLL
HHHHHHL
LLLH
HHLLLLL
LLHL
HLHHLHH
LLHH
HHHLLHH
LHLL
HHLLHLH
LHLH
LHHLHHH
LHHL
LHHHHHH
LHHH
HHLLLHL
HLLL
HHHHHHH
19
HLLH
HHHLHHH
LLLL
LLLLLLL
library ieee; use ieee.std_logic_1164.all; entity convertidor is port( bcd: in bit_vector(3 downto 0); led: out bit_vector(6 downto 0) ); end convertidor; architecture archiconv of convertidor is begin conv: process (bcd) begin case bcd is when "0000" => LED <= "1111110"; when "0001" => LED <= "1100000"; when "0010" => LED <= "1011011"; when "0011" => LED <= "1110011"; when "0100" => LED <= "1100101"; when "0101" => LED <= "0110111"; when "0110" => LED <= "0111111"; when "0111" => LED <= "1100010"; when "1000" => LED <= "1111111"; when "1001" => LED <= "1110111"; when others => LED <= "0000000"; -- si no se introduce BCD no se enciende ning煤n led end case; end process conv; end archiconv; --Declaraci贸n de un flip flop JK library ieee; use ieee.std_logic_1164.all; USE WORK.rtlpkg.ALL; ENTITY ffjk IS PORT ( j : IN std_logic; k : IN std_logic; clk : IN std_logic; q : OUT std_logic ); END ffjk; ARCHITECTURE archffjk OF ffjk IS SIGNAL qx, dx : std_logic; BEGIN dx <= (j and not(qx)) or (not(k) and qx); q <= qx; d1:dff port map(dx,clk,qx); END archffjk;
20
En el caso del IF se debe terminar con un ELSE. Y en el caso del CASE se debe de terminar con un ʺwhen others
Si es un flip‐flop activo por flanco de subida la sentencia de reloj debe ser: if clk'event and clk='1' then • Si es un flip‐flop activo por flanco de bajada la sentencia de reloj debe ser: if clk'event and clk='0' then
2#11000100# y 16#C4# Caracteres: Es cualquier letra o caracter entre comillas simples: 'l','3','t'. Cadenas: Son un conjunto de caracteres englobados por comillas dobles: "Esto es una cadena". Ejemplo: B"11101001", O"126", X"FE". & (concatenacion) 4**2 es 42 ABS() * (multiplicacion) / (division) MOD (modulo) a=b*N+(a MOD b) REM (resto) a=(a/b)*b+(a REM b) + (suma y signo positivo) - (resta y signo negativo) SLL, SRL Por ejemplo dato SLL 2 desplaza a izquierda el vector dato, es decir, lo multiplica por 4. SLA, SRA (desplazamiento aritmetico a izquierda y derecha) ROL, ROR (rotacion a izquierda y a derecha) =, /= <,<=,>,>= (menor mayor) 2#11000100# es un número en base 2, 16#c4# en hexadecimal Caracteres: Cualquier letra o número entre comillas simples: '2', 't'. Cadenas: Conjunto de caracteres en comillas dobles: "hola" <= := de asignación, el primero para señales, el segundo para constantes y variables. ABS() valor absoluto
21
* multiplicación / división MOD (módulo) REM (resto) SLL, SRL Desplaza un vector de bits un número de bits a la izquierda o a la derecha, rellenando con ceros los huecos libres SLA, SRA Como el anterior pero el desplazamiento conserva el signo, el valor del bit más significativo. ROL, ROR rotación a izquierda o a derecha. Como un desplazamiento, pero los huecos que se forman son ocupados por los bits que van saliendo. ('U', -- Sin inicializar 'X', -- Fuerza a desconocido '0', -- fuerza a 0 '1', -- fuerza a 1 'Z', -- Alta impedancia 'W', -- Desconocido débil 'L', -- 0 débil 'H', -- 1 débil
Las asignaciones de señales se realizan con el operador "<=", mientras que las de constantes y variables utilizan el operador ":=".
library ieee; use ieee.std_logic_1164.all; entity BCD_9 is port( A: in std_logic_vector(3 downto 0); Y: out std_logic_vector(9 downto 0)); end BCD_9; architecture archBCD_9 of BCD_9 is begin Y<="0000000001" when A="0000" else "0000000010" when A="0001" else "0000000100" when A="0010" else "0000001000" when A="0011" else "0000010000" when A="0100" else "0000100000" when A="0101" else "0001000000" when A="0110" else "0010000000" when A="0111" else "0100000000" when A="1000" else "1000000000" when A="1001" else "0000000000"; end archBCD_9; Es también una estructura concurrente. El codificador BCD a 10 líneas queda: library ieee; use ieee.std_logic_1164.all; entity BCD_9 is port( A: in std_logic_vector(3 downto 0); Y: out std_logic_vector(9 downto 0)); end BCD_9; architecture archBCD_9 of BCD_9 is begin with A select Y<="0000000001" when "0000", "0000000010" when "0001",
22
"0000000100" when "0010", "0000001000" when "0011", "0000010000" when "0100", "0000100000" when "0101", "0001000000" when "0110", "0010000000" when "0111", "0100000000" when "1000", "1000000000" when "1001", "0000000000" when others; end archBCD_9; La utilización de else es opcional, pero si no se agotan todas las opciones, puede dar lugar a latches. Cuando se utilizan varios if then anidados, puede usarse una contracción de else if, elsif. En caso de usar la primera forma, es necesario cerrar el nuevo if que se crea. En caso de usar la forma contraída, no hay que cerrar ningún if adicional. Si la condición que se ha de cumplir se refiere a la detección de un flanco de reloj y va precedida de otro if previo, es necesario usar elsif en la detección del flanco de reloj, por ejemplo, en un contador con reset asíncrono, la condición de detección del reset va antes de la detección de flanco de reloj. Es muy importante distinguir entre variables y señales dentro de un proceso. Las señales no cambian su valor hasta que no acaba el proceso. Las variables sólo se definen dentro del proceso, ( o dentro de bloques de ejecución en serie, como funciones y procedimientos) y cambian su valor en el momento en el que tienen una asignación. Por ejemplo, consideremos estos dos procesos: architecture ejemp1 of entidad is signal a,b,c,x,y: integer; begin p1: process(a,b,c) begin c<=a; -- se ignora x<=c+2; c<=b; y<=c+2; end process p1; end ejemp1; architecture ejemp1 of entidad is signal a,b,x,y: integer; begin p1: process (a,b) variable c: integer; begin c:=a; -- Inmediata x<=c+2; c:=b; -- Inmediata y<=c+2; end process p1; end ejemp1; En el código de la izquierda sólo se usan señales y la ejecución tiene lugar de la siguiente manera: En primer lugar se hace la fuente de "c" igual a "a", lo cual sólo indica que tomará ese valor en el próximo paso de simulación (al salir del proceso), pero no en el presente. A continuación se hace lo mismo con "x", asignándole a su fuente el valor de "c+2", es decir el valor que contuviera "c" antes de empezar la ejecución, porque el valor que se le asignó en el paso anterior todavía no está presente. Luego, se hace "c<=b", es decir que se está sustituyendo el valor de la fuente de "c", que era "a", por la señal "b". Esto quiere decir que el futuro valor de "c" ya no será "a", sino "b". A continuación se hace "y<=c+2", de manera que a "y" se le asigna el valor "c+2", pero tomando como "c" el valor de la señal antes de empezar el proceso. En definitiva, supongamos que antes de iniciarse la ejecución se tiene "c=2", "a=4", y "b=6". Entonces al final de la ejecución de este proceso se tendrá "c=b=6", "x=4", "y=4". De todas formas, puesto que la señal "c" ha cambiado y, como se encuentra en la lista sensible, el proceso se vuelve a ejecutar, quedando finalmente "c=b=6", "x=y=8". Se ha visto que la primera instrucción "c<=a" es ignorada completamente en la práctica. Esto es
23
debido a que las señales no se asignan inmediatamente y a que existe una sentencia posterior "c<=b" que escribe sobre "c". En el programa de la derecha "c" es una variable. La definición de esta variable se hace dentro del proceso, puesto que sólo tiene sentido dentro de la ejecución serie. También "c" desaparece de la lista sensible puesto que las variables son internas a los procesos (o a los subprogramas) y nunca pueden formar parte de las listas sensibles. library ieee; use ieee.std_logic_1164.all; entity mux4to1 is port( a: in std_logic_vector(3 downto 0); sel: in std_logic_vector(1 downto 0); y: out std_logic); end mux4to1; architecture archmux4to1 of mux4to1 is begin p1: process (a,sel) begin case sel is when "00" => y<=a(0); when "01" => y<=a(1); when "10" => y<=a(2); when "11" => y<=a(3); when others => y<=a(0); end case; end process; end archmux4to1; Contador ascendente-descendente con carga paralelo síncrona y reset asíncrono: library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; use ieee.std_logic_unsigned.all; entity contador is port( clk,load,up,reset: in std_logic; d: in std_logic_vector(7 downto 0); q: buffer std_logic_vector(7 downto 0)); end entity; architecture archcontador of contador is begin p: process(clk,d,q,load,up,reset) begin if reset='1' then q<=x"00"; elsif clk'event and clk='1' then if load='1' then q<=d; elsif up='1' then q<=q+1; else q<=q-1; end if; end if; end process; end architecture;
24
25
26
27
28