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