Aspectos funcionales
Partimos de 5 leds de alta luminosidad (los mismos de años anteriores) y necesitamos que parpadeen de forma aleatoria, como si simularan el aspecto de una porción del cielo nocturno. La secuencia de parpadeo debería ser lo más aleatoria posible y lo ideal es que la probabilidad de parpadeo sea controlable para simular un ciclo día-noche.
Diseño
Para generar una secuencia de números pseudoaleatorios la forma más sencilla es utilizar un LFSR con la cantidad suficiente de bits como para dar la percepción de que se trata de un generador de números realmente aleatorios. Si partimos de un LFSR de 10 bits, para que sea maximal (que su secuencia numérica sea lo más larga posible antes de “dar la vuelta”) debemos implementar el siguiente polinomio de realimentación:
$$x^{10} + x^7 + 1$$
Este polinomio de realimentación garantiza una secuencia maximal de $2^{n} - 1$ valores, siendo en este caso $n=10$. La secuencia no es de $2^{n}$ valores debido a que el valor 0 (todos los bits a cero) no está incluido en la secuencia.
("=1" denota la operación XOR en notación IEC) La ruta de datos que se va a usar es la siguiente:
El funcionamiento interno sería el siguiente.
1. Se inicializa el LFSR (se le mete un valor que incluya, al menos un bit a 1).
2. Hacer 5 veces (una vez por cada uno de los 5 leds).
2.1. Se itera el LFSR para que genere el siguiente numero pseudoaleatorio.
2.2. Se empuja el bit resultante de la comparación entre el valor del LFSR (valor A) y una constante (valor B) en el registro de desplazamiento.
3. Se carga en el latch de salida el valor que hay en el registro de desplazamiento.
4. Se espera 1 segundo.
5. Saltar al paso 2.
Tanto para el conteo de la carga de los 5 bits en el registro de desplazamiento como para el conteo del tiempo de espera de 1 segundo se utiliza un contador de 32 bits de dos límites: uno de los límites se fija a 5 (para contar los bits) y otro de los límites se fija en 32000000 para contar 1 segundo (el reloj de la FPGA va a 32MHz).
A partir de este algoritmo se puede diseñar la siguiente máquina de estados:
Salidas de la FSM:
LFSR.RST = Reset del LFSR.
LFSR.ENA = Enable del LFSR.
SR.ENA = Enable del registro de desplazamiento.
LATCH.ENA = Enable del latch de salida.
CNT.RST = Reset del contador.
CNT.ENA = Enable del contador.
Entradas de la FSM:
CNT.T1 = a 1 cuando el contador llega a 5.
CNT.T2 = a 1 cuando el contador llega a 32000000.
Como se puede ver, se trata de un diseño totalmente síncrono, basado en enables y en el que se evita el uso de “gated clocks”, por lo tanto, perfectamente sintetizable en cualquier FPGA.
Por ahora la probabilidad de parpadeo está fijada por hardware como una constante (el valor de B en el diagrama, que no es modificable), sin embargo el diseño queda preparado para que en una siguiente versión se pueda obtener dicha constante de algún parámetro físico (ADC, reloj de tiempo real, etc.)
Implementación
La implementacion de todos los módulos se ha realizado siguiendo siempre un modelo RTL. A continuación se lista el codigo fuente de la unidad de más alto nivel (que se ha denominado “ChristmasLights”) y que engloba todos los submódulos (LFSR, comparador, registro de desplazamiento, latch, contador y FSM).
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity ChristmasLights is generic ( NLeds : integer := 8; NWaitClocks : integer := 20; -- for the simulation 20 clocks between lights change, but in real hardware change this value according FPGA clock Probability : integer := 512 -- 0 = all lights on, 1023 = all lights off ); port ( Clk : in std_logic; Reset : in std_logic; Led : out std_logic_vector((NLeds - 1) downto 0) ); end ChristmasLights; architecture Architecture1 of ChristmasLights is component LFSR10 is port ( Reset : in std_logic; Enable : in std_logic; Clk : in std_logic; Data : out std_logic_vector(9 downto 0) ); end component; component Comparator is generic ( NBits : integer := 4 ); port ( A : in std_logic_vector((NBits - 1) downto 0); B : in std_logic_vector((NBits - 1) downto 0); AGreatThanB : out std_logic; ALessThanB : out std_logic; AEqualB : out std_logic ); end component; component ShiftRegister is generic ( NBits : integer := 8 ); port ( Enable : in std_logic; Clk : in std_logic; SerialInput : in std_logic; ParallelOutput : out std_logic_vector((NBits - 1) downto 0) ); end component; component Latch is generic ( NBits : integer := 8 ); port ( Enable : in std_logic; Clk : in std_logic; DataIn : in std_logic_vector((NBits - 1) downto 0); DataOut : out std_logic_vector((NBits - 1) downto 0) ); end component; component TwoLimitCounter is generic ( NBits : integer := 4; Limit1 : integer := 3; Limit2 : integer := 2 ); port ( Reset : in std_logic; Enable : in std_logic; Clock : in std_logic; Terminated1 : out std_logic; Terminated2 : out std_logic ); end component; signal LfsrEnable : std_logic; signal LfsrReset : std_logic; signal LfsrData : std_logic_vector(9 downto 0); signal CompOutput : std_logic; signal SREnable : std_logic; signal SRData : std_logic_vector((NLeds - 1) downto 0); signal LatEnable : std_logic; signal CntReset : std_logic; signal CntEnable : std_logic; signal CntBitsOut : std_logic; signal CntTimeOut : std_logic; signal FSMDBus : std_logic_vector(2 downto 0); signal FSMQBus : std_logic_vector(2 downto 0); begin -- LFSR Lfsr : LFSR10 port map ( Clk => Clk, Enable => LfsrEnable, Reset => LfsrReset, Data => LfsrData ); -- comparator Comp : Comparator generic map ( NBits => 10 ) port map ( A => LfsrData, B => std_logic_vector(to_unsigned(Probability, 10)), AGreatThanB => CompOutput ); -- shift register SR : ShiftRegister generic map ( NBits => NLeds ) port map ( Enable => SREnable, Clk => Clk, SerialInput => CompOutput, ParallelOutput => SRData ); -- output latch Lat : Latch generic map ( NBits => NLeds ) port map ( Enable => LatEnable, Clk => Clk, DataIn => SRData, DataOut => Led ); -- two limit counter Cnt : TwoLimitCounter generic map ( NBits => 32, Limit1 => NLeds, Limit2 => NWaitClocks ) port map ( Reset => CntReset, Enable => CntEnable, Clock => Clk, Terminated1 => CntBitsOut, Terminated2 => CntTimeOut ); -- FSM D FFs process (Clk, Reset) begin if (Clk'event and (Clk = '1')) then if (Reset = '1') then FSMQBus <= (others => '0'); else FSMQBus <= FSMDBus; end if; end if; end process; -- FSM next state logic FSMDBus <= "000" when (Reset = '1') else "001" when (FSMQBus = "000") else "010" when (FSMQBus = "001") or (FSMQBus = "011") else "011" when (FSMQBus = "010") and (CntBitsOut = '0') else "100" when (FSMQBus = "010") and (CntBitsOut = '1') else "101" when (FSMQBus = "100") or ((FSMQBus = "101") and (CntTimeOut = '0')) else "001" when (FSMQBus = "101") and (CntTimeOut = '1') else "000"; -- FSM output logic LfsrReset <= '1' when (FSMQBus = "000") else '0'; CntReset <= '1' when (FSMQBus = "001") or (FSMQBus = "100") else '0'; CntEnable <= '1' when (FSMQBus = "010") or (FSMQBus = "101") else '0'; LfsrEnable <= '1' when (FSMQBus = "010") else '0'; SREnable <= '1' when (FSMQBus = "011") else '0'; LatEnable <= '1' when (FSMQBus = "100") else '0'; end Architecture1;
Vídeo con el código VHDL implementado sobre la FPGA Spartan3E de Xilinx.
Todo el codigo puede descargarse de la sección soft. Feliz programación y feliz Navidad :-).
[ añadir comentario ] ( 1784 visualizaciones ) | [ 0 trackbacks ] | enlace permanente | ( 3 / 2637 )