1
votes

This is a problem I regularly bump into - hopefully someone can clarify to me the right way to think about it!

I can basically reduce my problem to this: I have a module, with input p_in, output p_out and clock clk. The functionality is that whenever in goes low - out should produce a high pulse with a duration of one clock period; and this "negative edge detector" is implemented as an FSM with four states: reset; wt_in_lo (wait for input low); sample_signal (generate high signal on output, upon detected transition to zero on input); wt_in_hi (wait for input high, after sample signal has been raised).

The code is included below, and the result in the isim simulator looks like this (and this is behavioral simulation, so there shouldn't be any platform-specific translation influence):

fsm_react_test_annot.png

Basically, the states do change as I expect them to - first clock posedge, the FSM recognizes in is low, so it transits to sample_signal; second clock posedge, we are in sample_signal state.

  • However, here is my problem - I'd expect for the out to go high as soon as the sample_signal state is entered (or at least, that's what I'm trying to achieve); yet, the out doesn't perform the high pulse until the next state (wt_in_hi) is entered!

And then I usually try to code around this (i.e. setting out in the synchronous fsm part, which is probably a bad idea) - and end up confusing both the synthesizer and myself :)

 

So, in brief - is it possible that I get an out signal as soon as the second state is entered (and for the duration of second state); and what would be the proper way to code it?

Many thanks in advance for any answers,
Cheers!

 

The code:

-- file: fsm_react_test_twb.vhd
---------------
-- single file testbench examples - see also;
-- http://www.cs.umbc.edu/portal/help/VHDL/samples/samples.shtml

library IEEE;
  use IEEE.STD_LOGIC_1164.ALL;
  use IEEE.NUMERIC_STD.ALL;

-- NEVER call port pins "in"; "out": ERROR:HDLCompiler:806 'Syntax error near "in"'!
ENTITY negedgeDetector IS
  PORT (
    clk: IN STD_LOGIC;
    p_in : IN STD_LOGIC;
    p_out: OUT STD_LOGIC
  );
END negedgeDetector;


ARCHITECTURE structure OF negedgeDetector IS

  -- 'dummy signals' - registers
  SIGNAL w_in : STD_LOGIC := 'Z';
  SIGNAL w_out : STD_LOGIC := 'Z';

  -- fsm states
  TYPE states_ned IS -- ned: negedgeDetector
  (
    ned_reset,
    ned_wt_in_lo,       -- wait for in active low
    ned_sample_signal,  -- signal for sampling
    ned_wt_in_hi        -- wait for in active hi
  );

  -- init fsm state vars
  SIGNAL state_ned, next_state_ned: states_ned := ned_reset;

-- implementation:
BEGIN

  -- assign 'wire' / registers
  w_in <= p_in;
  p_out <= w_out;


  -- STATE MACHINES CODE =========
  sm_ned: PROCESS(state_ned, w_in) -- combinatorial process part
  BEGIN
    CASE state_ned IS

      WHEN ned_reset =>
        next_state_ned <= ned_wt_in_lo;

      WHEN ned_wt_in_lo =>
        IF w_in = '0' THEN
          next_state_ned <= ned_sample_signal;
        ELSE
          next_state_ned <= ned_wt_in_lo;
        END IF;

      WHEN ned_sample_signal =>
        next_state_ned <= ned_wt_in_hi;

      WHEN ned_wt_in_hi =>
        IF w_in = '0' THEN
          next_state_ned <= ned_wt_in_lo;
        ELSE
          next_state_ned <= ned_wt_in_hi;
        END IF;

    END CASE;
  END PROCESS sm_ned;

  out_sm_ned: PROCESS(clk) -- synchronous process part --
  BEGIN
    IF (rising_edge(clk)) THEN -- returns only valid transitions;
      IF state_ned = ned_sample_signal THEN
        -- signal for sampling
        w_out <= '1';
      ELSE
        w_out <= '0';
      END IF;

      state_ned <= next_state_ned;
    END IF;
  END PROCESS out_sm_ned;

  -- END STATE MACHINES CODE =====
END structure; -- ARCHITECTURE


-- #########################

library IEEE;
  use IEEE.STD_LOGIC_1164.ALL;
  use IEEE.NUMERIC_STD.ALL;

ENTITY fsm_react_test_twb IS
END fsm_react_test_twb;

ARCHITECTURE testbench_arch OF fsm_react_test_twb IS

  COMPONENT negedgeDetector
    PORT(
      clk:       IN STD_LOGIC;
      p_in :       IN STD_LOGIC;
      p_out:       OUT STD_LOGIC
    );
  END COMPONENT;

  -- 'wires'
  SIGNAL wCLK : std_logic := '0';

  SIGNAL wIN  : std_logic := 'Z';
  SIGNAL wOUT : std_logic := 'Z';

  -- clock parameters
  constant PERIODN : natural := 20; -- can be real := 20.0;
  constant PERIOD : time := PERIODN * 1 ns;
  constant DUTY_CYCLE : real := 0.5;
  constant OFFSET : time := 100 ns;

-- implementation of workbench
BEGIN

  -- instances of components, and their wiring (port maps)...
  UUT : negedgeDetector -- VHDL
  PORT MAP(
    clk => wCLK,
    p_in  => wIN,
    p_out => wOUT
  );

  -- PROCESSES (STATE MACHINES) CODE =========

  -- clock process for generating CLK
  clocker: PROCESS
  BEGIN

    WAIT for OFFSET;

    CLOCK_LOOP : LOOP
      wCLK <= '0';
      WAIT FOR (PERIOD - (PERIOD * DUTY_CYCLE));
      wCLK <= '1';
      WAIT FOR (PERIOD * DUTY_CYCLE);
    END LOOP CLOCK_LOOP;
  END PROCESS clocker;

  simulator: PROCESS
  BEGIN

    WAIT for OFFSET;

    WAIT for 10 ns;

    -- take 'in' low - out should detect it with a pulse
    wIN <= '0';
    WAIT for 50 ns;

    -- take 'in' high - no out
    wIN <= '1';
    WAIT for 50 ns;

    -- repeat
    wIN <= '0';
    WAIT for 50 ns;

    wIN <= '1';
    WAIT for 50 ns;

    -- hold
    WAIT;

  END PROCESS simulator;

  -- END PROCESSES (STATE MACHINES) CODE =====
END testbench_arch; -- ARCHITECTURE



-----------------------
-- call with (Xilinx ISE WebPack 13.2 tools):

-- # note: -tclbatch (isim script) _needs_ -view (*.wcfg) to run! (empty *.wcfg ok)
-- # must use isim script to run at start (and setup view)..

-- # first run:
-- echo 'vhdl work "fsm_react_test_twb.vhd"' > fsm_react_test_twb.prj
-- touch fsm_react_test_twb.wcfg
-- echo -e "wave add {/fsm_react_test_twb/wclk}\nwave add {/fsm_react_test_twb/win}\nwave add {/fsm_react_test_twb/wout}\nwave add {/fsm_react_test_twb/UUT/state_ned}\nwave add {/fsm_react_test_twb/UUT/next_state_ned}\nrun 500 ns\n" > fsm_react_test_twb.isim

-- # build sim and run:
-- fuse -o fsm_react_test_twb.exe -prj fsm_react_test_twb.prj work.fsm_react_test_twb
-- ./fsm_react_test_twb.exe -gui -tclbatch fsm_react_test_twb.isim -view fsm_react_test_twb.wcfg
2
You may want to google the word "schedule" with VHDL. You schedule the output value to be presented on the next clock (well, the next change of a signal in the sensitivity list).Aaron D. Marasco
Many thanks for that @AaronD.Marasco - will definitely read up on that! Cheers!sdaau

2 Answers

3
votes

The problem is that you are checking the value of state_ned from within a synchronous process. I.e. the output will not change before the next rising clock edge.

If you want to keep the output synchronous you would have to check the value of next_state_ned, e.g.:

out_sm_ned: PROCESS(clk) -- synchronous process part --
BEGIN
  IF (rising_edge(clk)) THEN -- returns only valid transitions;
    IF next_state_ned = ned_sample_signal THEN
      -- signal for sampling
      w_out <= '1';
    ELSE
      w_out <= '0';
    END IF;

    state_ned <= next_state_ned;
  END IF;
END PROCESS out_sm_ned;
0
votes

I don't see a need for a state machine here. My approach would be to have a single flop and an AND gate:

proc_reg: process (clk,reset)
begin
  if reset = RESET_ACTIVE_LEVEL then

    q_r0 <= '0';

  elsif clk'event and clk = '1' then

    q_r0 <= p_in;

  end if;
end process proc_reg;

p_out <= '1' when p_in = '0' and q_r0 = '1' else '0';

If p_in is not synchronous with clk, then you can add additional register stages in front of q_r0 to clean up any meta-stability.