Generador de números aleatorios en VHDL

 

En esta serie de artículos voy a presentar varios ejemplos de VHDL. Comenzando por un ejemplo simple de generador de números aleatorios, le iremos agregando más funciones, por lo que en este tutorial veremos:

  • Código inicial del generador y mejoras sucesivas
  • Banco de pruebas para las diversas versiones (test bench)
  • Parametrización del diseño
  • Grabación de los resultados de la simulación en archivos
  • Cómo importar los archivos de resultado para analizarlos en Matlab.


Para poder aprovechar este aporte deben conocer previamente el lenguaje VHDL y el manejo de Modelsim u otro simulador que Uds. prefieran.

Primera Parte - El código inicial
Vamos a generar números aleatorios por hardware. Una manera popular de generar números aleatorios por hardware es utilizar lo que se denomina LFSR, registros de desplazamiento con realimentación lineal. El registro y su implementación lineal son equivalentes al desarrollo de un polinomio, donde cada potencia del polinomio es representada por el orden del bit dentro del registro.

Para el que no ha oído estos conceptos antes puede resultar un poco complicado. No se desanimen, al repasar los ejemplos prácticos será más digerible.

A continuación, la primera versión del código VHDL que genera números aleatorios.

 

 

Listado:
-- File name   : lfsr1.vhd
-- Comments    : Random number generator in VHDL - Version 1
-----------------------------------------------------------------
library ieee;
    use ieee.std_logic_1164.all;

entity lfsr1 is
  port (
    reset  : in  std_logic;
    clk    : in  std_logic;                    
    count  : out std_logic_vector (3 downto 0)
  );
end entity;

architecture rtl of lfsr1 is
    signal count_i      : std_logic_vector (3 downto 0);
    signal feedback     : std_logic;

begin
    feedback <= not(count_i(3) xor count_i(2));              -- LFSR size 4

    process (reset, clk) 
        begin
        if (reset = '1') then
            count_i <= (others=>'0');
        elsif (rising_edge(clk)) then
            count_i <= count_i(2 downto 0) & feedback;
        end if;
    end process;
    count <= count_i;
end architecture;

El código es bastante corto. La señal de feedback, en este primer ejemplo, está calculada tomando en cuenta un polinomio de orden 3 (4 bits), que genera una secuencia de 15 valores. Si generamos más valores que 15 la secuencia se repite indefinidamente.

Es por eso que a estas secuencias se las denomina pseudo aleatorias, por un lado presentan una cierta variabilidad, por otro, son repetitivas y si sabemos el orden del polinomio, a un valor determinado siempre le seguirá un valor conocido... Un número realmente aleatorio no se comporta así, pero igual veremos que la secuencia pseudo aleatoria es muy útil, por ejemplo, como generador de ruido blanco, o rosa.

En VHDL no se puede leer en el código el valor de una salida, es por eso que todos los cálculos los realizo con count_i, y solo al final vuelco su valor a la salida. Creo que es una buena costumbre y clarifica el código el utilizar esta convención de agregar el sufijo "_i" al nombre de la señal. Uds. pueden usar esa mismo convención u otra con la que se sientan cómodos, lo importante es adoptar una convención y después usarla siempre.

Dentro del proceso lo que estamos haciendo es un registro de desplazamiento cuya entrada (feedback) es una combinación lineal de algunos de los bits del registro mediante compuertas XOR.

 

 

Segunda parte - Banco de pruebas

Analicemos ahora el banco de pruebas, o testbench, para el contador pseudo aleatorio del mensaje anterior.

 

Listado:
-- File Name   : tb_lfsr1.vhd
--  Comments   : Test bench for the random number generation
--                               Version 1
------------------------------------------------------------------
library ieee;
    use ieee.std_logic_1164.all;
    use ieee.std_logic_textio.all;
    use ieee.numeric_std.ALL;
    use std.textio.all;
    
entity tb_lfsr1 is
end entity;

architecture test of tb_lfsr1 is

    constant PERIOD  : time   := 20 ns;
        
    signal clk       : std_logic := '0';
    signal reset     : std_logic := '1';
    signal count     : std_logic_vector (3 downto 0);
    signal endSim        : boolean   := false;

    component lfsr1 is
    port (
        reset     : in  std_logic;                       
        clk       : in  std_logic;               
        count     : out std_logic_vector (3 downto 0)   
    );
    end component;
    

begin
    clk     <= not clk after PERIOD/2;
    reset   <= '0' after  PERIOD*10;
    endSim  <= true after PERIOD*80;

        -- End the simulation
        process 
        begin
           if (endSim) then
              assert false 
                report "End of simulation." 
                severity failure; 
           end if;
           wait until (clk = '1');
        end process;    

    lfrs1_inst : lfsr1
    port map (
        clk      => clk,
        reset    => reset,
        count    => count
    );

end architecture;

 

El banco de pruebas incluye el elemento a ser verificado, que se define como componente. Además, definimos señales que nos ayudarán a excitar las entradas y observar las salidas.

 


Así, definimos un reloj de período 20 ns (50MHz), activamos el reset al comienzo de la prueba y lo desactivamos luego de un tiempo.

En este diseño tan simple, estando el reset inactivo y habiendo reloj, el circuito ya proporciona su salida.

He incluido además un proceso que culmina la simulación luego de un tiempo determinado mediante la señal endSim. Esto me permite correr la simulación mediante el comando run -all de Modelsim. 

A continuación dos screenshots que muestran la salida del generador.

Aquí podemos ver la salida del generador que en cierta medida recuerda a una señal de ruido. La señal no es muy satisfactoria ya que la secuencia es de sólo 15 pasos. Como podemos ver la señal, luego de recorridos quince valores, se repite. O sea, es periódica. Otro impedimento si lo que queremos es una señal de ruido. En próximos aportes veremos como obtener señales de ruido mejores.

En la imagen siguiente podemos ver un período de la señal de pseudo ruido y como recorre los quince valores de la secuencia.

Tercera Parte - Generador pseudo aleatorio mejorado

 

Vamos a mejorar un poco más nuestro generador de números aleatorios.

 

En general se prefiere no utilizar constantes en el cuerpo de nuestro programa, como las que definen el ancho del registro. El inconveniente de utilizar constantes es que si queremos cambiar nuestro programa, deberemos recordar actualizar todas y cada una de las ocurrencias del número constante... y es probable que cometamos un error al hacerlo. Por eso en lugar de utilizar números, se utilizan símbolos que representan a esos números constantes.

 

VHDL otorga al menos dos maneras de definir constantes. Una es el uso de GENERICS, y la otra es definir la constante en un paquete predefinido. Usaremos la segunda opción.

 

Esta versión del generador de pseudo-ruido, entonces, incluye un paquete de constantes predefinidas (por ahora tiene una sola constante), como vemos aquí

 

Listado:
-- File name   : lfsr1.vhd
------------------------------------------------------------------ 
library ieee; 
use ieee.std_logic_1164.all; 
use ieee.std_logic_textio.all; 
use ieee.numeric_std.ALL; 
use std.textio.all; 
package lfsr_pkg is 
   constant LFSR_W : natural := 9; -- LFSR width 
end lfsr_pkg;

El código en su versión 1.1, usa constantes predefinidas. La cláusula "use" con el nombre del paquete es la que permite utilizar las constantes definidas en tal paquete.

 

Listado:
-- File name     : lfsr1_1.vhd
--      Comments     : Random number generator in VHDL - Version 1.1
--
-- Revisions
--   v1:    Initial
--   v1.1:  Added enable signal
--          Constants defined on external package lfsr_pkg
-----------------------------------------------------------------
library ieee;
    use ieee.std_logic_1164.all;
        use work.lfsr_pkg.all;

entity lfsr1 is
  port (
    reset  : in  std_logic;
    clk    : in  std_logic; 
    enable : in  std_logic;                              -- Enable counting     
    count  : out std_logic_vector (LFSR_W-1 downto 0) -- lfsr output
  );
end entity;

architecture rtl of lfsr1 is
    signal count_i      : std_logic_vector (LFSR_W-1 downto 0);
    signal feedback     : std_logic;

begin
    feedback <= not(count_i(LFSR_W-1) xor count_i(LFSR_W-5));                -- LFSR size 9

    process (reset, clk) 
        begin
        if (reset = '1') then
            count_i <= (others=>'0');
        elsif (rising_edge(clk)) then
            if (enable = '1') then
                count_i <= count_i(LFSR_W-2 downto 0) & feedback;
            end if;     
        end if;
    end process;
    count <= count_i;
end architecture;

Qué otros cambios incluye esta versión?

1. Se agregó una señal de enable. Fíjense que es importante la manera de escribir código con enable, el estado de esta señal se verifica DENTRO del bucle condicionado al flanco creciente de la señal de reloj.

La señal de enable nos permitirá procesar señales de ruido a frecuencias diferentes de (aunque siempre menores o iguales que) la frecuencia de reloj.

2. Se cambió el tamaño del registro (ahora tiene 9 bits), y la lógica de generación de realimentación. La señal de pseudo ruido generada es mucho más parecido a lo que esperaríamos:

Notas:
a- Si miran con atención, verán que esta señal también es periódica. Pero el período es mucho más largo que el ejemplo analizado en la primera y segunda parte del artículo.


b- Como desafío para Uds., queda el adaptar el test bench a esta nueva versión. Se puede verificar que la secuencia para 9 bits es de 511 pasos, por lo que el período de esta señal de pseudo ruido es de 10220ns (clk 50MHz => 20ns para cada paso)


c- En el zip adjunto encontrarán las fuentes de los archivos de este capítulo

Nota: Las partes 4 y 5 de este tutorial, en la sección 2.

 

 

Archivos vhdl y test_bench v1_1
lfsr1_1.zip
Archivo comprimido formato ZIP 980 Bytes
Comentarios: 0

Temas Nuevos


22-Agosto

Cuento: Marte en Nueva York


17-Agosto

Humor: Más refranes de-formados


13-Agosto

Tres juegos nuevos: Curvy, Pentapuzzle y Pucked



8-Agosto

Acertijos matemáticos de Agosto


3-Agosto

Cuadracópteros atléticos y acrobáticos


2-Agosto

Nuevos desafíos en el juego de la Vida de Conway


2-Agosto

Capítulo 5 del Curso de PLCs



1ro-Agosto

La máquina de Antikhytera, hecha con Lego




26-Julio

Otro capítulo del Curso de PLCs


Visitantes: