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:
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.
-- 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.
-- 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í
-- 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.
-- 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