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.

Sumador combinacional de n bits

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.

Maquina de estados secuencial
Maquina de estados secuencial

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.

Reloj en un sistema digital
Reloj en un sistema digital

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