Search code examples
haskellinitializationvhdlfpgaclash

Initializing registers


I have a very simple synchronous circuit that is supposed to blink an LED:

module Blinker where

import Clash.Prelude
import Data.Word

{-# NOINLINE topEntity #-}
{-# ANN topEntity
  (Synthesize
    { t_name   = "blinkerTop"
    , t_inputs = [PortName "CLK_32MHZ", PortName "RESET"]
    , t_output = PortName "LED"
    }) #-}
topEntity
  :: Clock System Source
  -> Reset System Asynchronous
  -> Signal System Bit
topEntity = exposeClockReset $ tickTock 32000000

tickTock :: (HiddenClockReset domain gated synchronous) => Word32 -> Signal domain Bit
tickTock n = mealy step (False, 0) (pure ())
  where
    step (s, k) () =
        let k' = k + 1
            finished = k' == n
            s' = if finished then not s else s
            k'' = if finished then 0 else k'
        in ((s', k''), if s' then high else low)

Since it doesn't work when I upload it to a real FPGA board, I thought I'd try it in Xilinx's simulator, with the following testbench:

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;

ENTITY testbench IS
END testbench;

ARCHITECTURE behavior OF testbench IS 
    COMPONENT blinkerTop
    PORT(
         CLK_32MHZ : IN  std_logic;
         RESET : IN  std_logic;
         LED : OUT  std_logic
        );
    END COMPONENT;

   signal CLK_32MHZ : std_logic := '0';
   signal RESET : std_logic := '0';
   signal LED : std_logic;
   constant CLK_32MHZ_period : time := 31.2 ns;

BEGIN 
   uut: blinkerTop PORT MAP (
          CLK_32MHZ => CLK_32MHZ,
          RESET => RESET,
          LED => LED
        );

   CLK_32MHZ_process :process
   begin
        CLK_32MHZ <= '0';
        wait for CLK_32MHZ_period/2;
        CLK_32MHZ <= '1';
        wait for CLK_32MHZ_period/2;
   end process;
END;

In the simulator, this matches the real life FPGA board's behaviour: the LED signal stays low.

I looked at the generated VHDL and this is what it looks like:

-- Automatically generated VHDL-93
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
use std.textio.all;
use work.all;
use work.blinkertop_types.all;

entity blinkerTop is
  port(-- clock
       CLK_32MHZ : in std_logic;
       -- asynchronous reset: active high
       RESET     : in std_logic;
       LED       : out std_logic);
end;

architecture structural of blinkerTop is
  signal \#tup_app_arg\       : unsigned(31 downto 0);
  signal \s'\                 : boolean;
  signal \#s'_case_alt\       : boolean;
  signal s                    : boolean;
  signal \#finished_case_alt\ : boolean;
  signal \#k'_case_alt\       : unsigned(31 downto 0);
  signal ds                   : blinkertop_types.tup2;
  signal \#finished_app_arg\  : signed(63 downto 0);
  signal x                    : unsigned(63 downto 0);
  signal x_0                  : blinkertop_types.tup2;
  signal \x#\                 : unsigned(63 downto 0);
  signal k                    : unsigned(31 downto 0);
  signal \#w\                 : unsigned(63 downto 0);
begin
  LED <= '1' when \s'\ else
         '0';

  \#tup_app_arg\ <= resize(to_unsigned(0,64),32) when \#finished_case_alt\ else
                    \#k'_case_alt\;

  \s'\ <= \#s'_case_alt\ when \#finished_case_alt\ else
          s;

  \#s'_case_alt\ <= false when s else
                    true;

  s <= ds.tup2_sel0;

  \#finished_case_alt\ <= tagToEnum(\#finished_app_arg\);

  \#w\ <= (\x#\ + to_unsigned(1,64));

  \#k'_case_alt\ <= resize((resize(\#w\(31 downto 0),64)),32);

  -- register begin 
  blinkertop_register : process(CLK_32MHZ,RESET)
  begin
    if RESET = '1' then
      ds <= ( tup2_sel0 => false, tup2_sel1 => resize(to_unsigned(0,64),32) )
      -- pragma translate_off
      after 1 ps
      -- pragma translate_on
      ;
    elsif rising_edge(CLK_32MHZ) then
      ds <= x_0
      -- pragma translate_off
      after 1 ps
      -- pragma translate_on
      ;
    end if;
  end process;
  -- register end

  \#finished_app_arg\ <= to_signed(1,64) when x = to_unsigned(32000000,64) else to_signed(0,64);

  x <= resize(\#k'_case_alt\,64);

  x_0 <= ( tup2_sel0 => \s'\
         , tup2_sel1 => \#tup_app_arg\ );

  \x#\ <= resize(k,64);

  k <= ds.tup2_sel1;
end;

I noticed that the internal state is not initialized, only assigned on reset. So this gave me an idea to add a reset process to the testbench:

stim_proc: process
begin       
   RESET <= '1';  
   wait for 100 ns; 
   RESET <= '0';
   wait;
end process;

With this change, afterwards I see LED starting to blink in the simulator. [*]

That takes care of the simulation; but how would I ensure a similar reset signal on the real board?

[*] For the simulation, of course, I have increased the frequency from 32,000,000 cycles to just 100 cycles; otherwise I'd have to run the simulator for ages just to see the first transition.


Solution

  • One solution is to create a power-on-reset sequence in your FPGA logic. This can be implemented as a counter, and asserting reset as long as the counter value is below some constant. When the counter exceeds the constant value, deassert the reset.