LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;

ENTITY fsm_example IS
GENERIC (
  g_nof_inputs : NATURAL := 6
);
PORT (
  dp_rst               : IN STD_LOGIC;
  dp_clk               : IN STD_LOGIC;
  -- Inputs
  buf_busy             : IN STD_LOGIC;
  reg_record_all_rw    : IN STD_LOGIC;
  reg_record_enable_rw : IN STD_LOGIC;
  reg_dump_enables_rw  : IN STD_LOGIC_VECTOR(g_nof_inputs-1 DOWNTO 0);
  -- Outputs
  record_all           : OUT STD_LOGIC;
  record_enable        : OUT STD_LOGIC;
  dump_enables         : OUT STD_LOGIC_VECTOR(g_nof_inputs-1 DOWNTO 0)
);
END fsm_example;

ARCHITECTURE rtl OF fsm_example IS

  -- FSM states
  TYPE t_fsm IS (s_idle, s_recording, s_dumping);

  -- RTL state registers
  TYPE t_state IS RECORD
    fsm : t_fsm;
    -- other RTL state, not used in this example with only a FSM
  END RECORD;

  CONSTANT c_state_rst : t_state := (fsm => s_idle);  -- reset values

  SIGNAL q : t_state;  -- stored state with latency one
  SIGNAL d : t_state;  -- zero latency state

BEGIN

  -- p_state
  q <= d WHEN rising_edge(dp_clk);

  p_comb : PROCESS(dp_rst, q, buf_busy, reg_record_all_rw, reg_record_enable_rw, reg_dump_enables_rw)
    VARIABLE v : t_state;
  BEGIN
    -- Default
    v := q; -- keep values

    -- Other RTL functionality
    -- . not used in this example with only a FSM

    -- FSM
    CASE q.fsm IS
      WHEN s_idle =>
        record_all <= reg_record_all_rw;
        record_enable <= '0';
        dump_enables <= (OTHERS => '0');
        IF reg_record_enable_rw = '1' AND buf_busy = '0' THEN
          v.fsm := s_recording;
        END IF;
        IF UNSIGNED(reg_dump_enables_rw) > 0 AND buf_busy = '0' THEN
          v.fsm := s_recording;
          dump_enables <= reg_dump_enables_rw;
        END IF;
      WHEN s_recording =>
        IF reg_record_enable_rw = '1' THEN
          record_enable <= '1';
        ELSE
          v.fsm := s_idle;
          record_enable <= '0';
        END IF;
      WHEN s_dumping =>
        IF UNSIGNED(reg_dump_enables_rw) = 0 THEN
          v.fsm := s_idle;
          dump_enables <= (OTHERS => '0');
        END IF;
      WHEN OTHERS =>
        -- try to recover from undefined FSM state
        v.fsm := s_idle;
    END CASE;

    -- Reset (synchronous)
    IF dp_rst = '1' THEN
      v := c_state_rst;
    END IF;

    -- Result
    d <= v;
  END PROCESS;

END rtl;