Hola a todos!
Si habéis visitado el enlace a nuestro curso de Udemy sobre VHDL y FPGA habréis visto nuestros perfiles y sabréis que ambos autores de este blog somos profesores en la Universidad y damos clase de Sistemas Digitales. En las prácticas usamos Modelsim y VHDL para simular circuitos que previamente hemos hecho en clase. Tras bastantes años enseñando conocemos de primera mano las dificultades con las que se encuentran los principiantes en VHDL, ya que los errores se repiten curso tras curso.
En este y posteriores artículos queremos mostraros cuales son estos errores típicos para que no os ocurran también a vosotros.
El primer error que queremos tratar es el de las listas de sensibilidad incompletas o erroneas.
Todavía no hemos tratado el el blog que son los sistemas combinacionales o secuenciales, que es fundamental para entender las listas de sensibilidad. A modo de resumen rápido, un sistema combinacional es un circuito donde la salida solo depende del valor de las entradas en ese momento (es una combinación de ellas) y un sistema secuencial depende de la secuencia de entradas que haya habido a lo largo del tiempo. Es decir, el sistema secuencial guarda memoria de lo que ha ocurrido anteriormente.
Un ejemplo de sistema combinacional puede ser un sumador donde sí las entradas valen 5 y 7 la salida será 12 independientemente del valor que hayan tomado anteriormente.

Un ejemplo de sistema secuencial puede ser un contador o una máquina de estados. En el contador cada instante de tiempo, marcado por el reloj, se incrementa en uno el valor que contiene. La salida depende del valor del instante anterior y por tanto tienen memoria.

La lista de sensibilidad de un proceso en VHDL, indica que señales está “escuchando el proceso”. Cuando alguna de estas señales cambia, el proceso se ejecuta y recalcula las salidas. Cada re-ejecución del proceso se llama ciclo delta y no implica un avance del tiempo de simulación. En el momento en que el circuito es estable, el tiempo de simulación avanza, aparecen nuevos cambios en las señales y otra vez empieza la ejecución de los ciclos delta.
Para modelar un sistema combinacional el proceso debe tener en la lista de sensibilidad todas las señales (eso incluye puertos) que se lean dentro del proceso, eso quiere decir que cada vez que alguna entrada al proceso cambie, este se reejecutará.
Vamos a verlo para un ejemplo de multiplexor de 4 entradas. En él hay una entrada x de 4 bits y una señal c de control que elige que bit de x se colocará a la salida en el puerto s.
El proceso que modela este comportamiento se puede realizar de múltiples formas. En este caso hemos optado por un proceso con una construcción case. La lista de sensibilidad al ser un proceso combinacional debe contener todos los puertos y señales que se lean dentro del proceso, en este caso x y c.
Si en el proceso secuencial no incluimos todas las señales que se leen dentro del proceso o incluimos señales extras, el simulador no va a dar errores y vamos a obtener una simulación, en ocasiones puede que correcta depende de como esté hecho el fichero de test. El problema viene cuando llevamos el circuito a la herramienta de síntesis, ya que lo que obtendremos son elementos de memoria indeseados, ya que por cada señal que omitamos en la lista de sensibilidad, la herramienta de síntesis incluirá un biestable disparado por nivel o latch, haciendo que el diseño final no sea correcto.
library ieee; use ieee.std_logic_1164.all; entity mux_4 is port (x : in std_logic_vector(3 downto 0); c : in std_logic_vector(1 downto 0); s : out std_logic ); end entity; architecture funcional of mux_4 begin process(x,c) begin case (c) is when "00" => s <= x(0); when "01" => s <= x(1); when "10" => s <= x(2); when others => s <= x(3); end case; end process; end architecture;
En un diseño secuencial, lo que tenemos que modelar son elementos de memoria que guarden valores y calculen nuevos datos a partir de ellos. Estos elementos de memoria son los biestables o flip-flops y en VHDL se modelan con un proceso que se carga únicamente cuando llega un flaco de reloj.
Vamos a ver el ejemplo que hemos comentado de contador, que cada ciclo de reloj incrementa la cuenta en uno.

En este caso tenemos una entrada de reloj, y un reset. El contador sumara uno al valor de cuenta cada vez que haya un flanco de reloj. En este artículo estamos hablando de listas de sensibilidad. Podemos ver cómo la lista de sensibilidad del proceso secuencial es unicamente clk y reset, no tiene ningún otro elemento.
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity contador is port (clk : in std_logic; reset : in std_logic; cuenta : out std_logic_vector(7 downto 0) ); end entity; architecture funcional of contador signal cuenta_s : std_logic_vector(7 downto 0); begin cuenta <= cuenta_s; process(clk,reset) begin if(reset='1') then cuenta_s <= (others=>'0'); elsif(clk'event and clk='1') then cuenta_s <= cuenta_s +1; end if; end process; end architecture;
Y para los que os preguntáis por qué incluimos una señal de cuenta. La respuesta es que los puertos de salida de una entidad no se pueden leer. Necesitamos cuenta_s que modela un registro, es ahí donde se almacenarán los valores.
Esperamos que este artículo os haya servido para entender mejor las listas de sensibilidad en VHDL.
Un saludo
Sobre el asunto de la lista de sensibilidad
Cuando process no tiene dicha lista, hasta donde sé… el proceso se dispara inmediatamente. Pero no entiendo el por qué de que las señales no tengan un comportamiento lógico, o al menos que pueda entender. Por ejemplo, dado el siguiente código…
library IEEE;
use IEEE.std_logic_1164.all;
entity crono1 is
end entity crono1;
architecture crono1 of crono1 is
signal x1, x2, x3, X4, X5 : std_logic;
begin
X1 <= '0', '1' after 10 ns,
'0' after 15 ns,'1' after 25 ns,
'0' after 30 ns;
Proc1: process
begin
x2 <= '1';
wait for 5 ns;
x2 <= '0';
wait for 10 ns;
x2 <= '1';
wait for 5 ns;
x2 <= '0';
end process;
x3 <= x1 after 5 ns;
Proc2: process
variable v1 : std_logic;
begin
for i in 0 to 3 loop
v1 := x1 xor x2;
X4 <= v1;
X5 <= X4;
wait for 5 ns;
end loop;
wait;
end process;
end architecture crono1;
¿Me puedes explicar porque las señales se comportan de esta manera?
No entiendo la razón de sus valores.
Gracias
Hola,
¿A que manera te refieres?
Ten en cuenta que una señal es algo mas complejo que una variable y cuando le asignas un valor no lo toma inmediatamente, sino que espera a que termina el ciclo delta actual.
En el caso del loop el valor de X4 no cambia a v1 inmediatamente, deberias usar variables intermedias.
No se si te referias a eso
Un saludo