warrier mahesh
warrier mahesh

Reputation: 3

how to avoid delay in the output of simple process statement in VHDL

i am a beginner in VHDL. i want ot know why there is a delay of one cycle in the following code.and how to avoid it..at the same time in verilog the statement always @(posedge clk) dont have any delay.. how to do the same in VHDL

library IEEE;
 use IEEE.std_logic_1164.all;

   -- entity
entity t_ff_s is
port ( T,S,CLK : in std_logic;
Q : out std_logic);
 end t_ff_s;
 -- entity
 architecture my_t_ff_s of t_ff_s is
signal t_tmp : std_logic; -- intermediate signal declaration
  begin
    tff: process (S,rising_edge(clk))
     begin
        if (S = '0') then
     t_tmp <= '1';
      --elsif (rising_edge(CLK)) then
     else 
     t_tmp <= T XOR t_tmp; -- temp output assignment
     end if;
    end process tff;
     Q <= t_tmp; -- final output assignment
     end my_t_ff_s;

Upvotes: 0

Views: 4601

Answers (2)

user1155120
user1155120

Reputation:

The problem

A little more succinctly than Kevin, rising_edge is an expression and not a signal, a sensitivity list requires a named signal, a transaction on which you resume execution of a suspended process. Put the elsif back in and have only S and clk in the sensitivity list.

Note that because t_tmp isn't in the sensitivity list, you won't see Q follow t_tmp until the next clock event causing the delay you noted.

The fixed syntax process:

     tff: process (S,clk) -- was (S, risingedge(CLK)), a syntax error)
     begin
        if (S = '0') then
     t_tmp <= '1';
      elsif (rising_edge(CLK)) then  -- put back
     -- else
     t_tmp <= T XOR t_tmp; -- temp output assignment
     end if;
      Q <= t_tmp; -- final output assignment
      end process tff;

Which shows the delay between t_tmp and Q:

t_ff_s half clock delay (clickable)

Fix it by making Q a concurrent signal assignment

To cure the half clock delay you could make the assignment to Q a concurrent signal assignment statement (move it outside of the process).

tff:
    process (S, clk)
    begin
        if S = '0' then
            t_tmp <= '1';
        elsif  rising_edge(clk) then
            t_tmp <= T xor t_tmp;
        end if;
    end process;
    Q <= t_tmp;   -- concurrent signal assignment

Which gives:

make Q assignment concurrent (clickable)

And you can see above that t_tmp and Q are now in phase.

Fix it by making t_tmp a variable

You could also declare t_tmp as a variable in process dff instead of a signal and switching assignments to it as variable assignments will also cure the one clock delay between t_tmp and Q.

tff:
    process (S, clk)
        variable t_tmp:  std_logic;
    begin
        if S = '0' then
            t_tmp := '1';
        elsif  rising_edge(clk) then
            t_tmp := T xor t_tmp;
        end if;
        Q <= t_tmp;
    end process;

Which shows:

tmp a variable (clickable)

And ghdl using gtkwave doesn't output variables or show delta cycles. You can see Q occurs on the rising edge of the clock.

Making t_tmp a variable also has the effect of eliminating a delta cycle between a transaction on t_tmp and a transaction on Q.

Eliminating delta cycles makes your model execute faster (while occurring at the current simulation time). Signal assignments don't take effect while any process is executing and variable assignments take effect immediately.

Fix it by adding t_tmp to the sensitivity list

And alternatively you could just add t_tmp to the sensitivity list (along with S and clk).

tff:
    process (S, clk, t_tmp)
    begin
        if S = '0' then
            t_tmp <= '1';
        elsif  rising_edge(clk) then
            t_tmp <= T xor t_tmp;
        end if;
        Q <= t_tmp;
    end process;

just add t_tmp to the sensitivity list (clickable)

And this is slower than all the rest of the fixes because the if statement is executed each time t_tmp has an event as well as S or CLK. rising_edge is a function call which dynamically elaborates it's interface list, a significant simulator performance penalty particularly if you use a lot of these primitives.

These were done with a test bench:

library IEEE;
 use IEEE.std_logic_1164.all;

   -- entity
entity t_ff_s is
port ( T,S,CLK : in std_logic;
Q : out std_logic);
 end entity t_ff_s;

architecture my_t_ff_s of t_ff_s is
signal t_tmp : std_logic; -- intermediate signal declaration
  begin
    tff: process (S,clk) -- was (S, risingedge(CLK)), a syntax error)
     begin
        if (S = '0') then
            t_tmp <= '1';
        elsif (rising_edge(CLK)) then  -- put back
        -- else
            t_tmp <= T XOR t_tmp; -- temp output assignment
        end if;
             Q <= t_tmp; -- final output assignment
    end process tff;
end my_t_ff_s;

architecture foe of t_ff_s is
    signal t_tmp: std_logic;
begin
tff:
    process (S, clk)
    begin
        if S = '0' then
            t_tmp <= '1';
        elsif  rising_edge(clk) then
            t_tmp <= T xor t_tmp;
        end if;
    end process;
    Q <= t_tmp;   -- concurrent signal assignment
end architecture;

architecture fie of t_ff_s is
begin
tff:
    process (S, clk)
        variable t_tmp:  std_logic;
    begin
        if S = '0' then
            t_tmp := '1';
        elsif  rising_edge(clk) then
            t_tmp := T xor t_tmp;
        end if;
        Q <= t_tmp;
    end process;
end architecture;

architecture fee of t_ff_s is
    signal t_tmp: std_logic;
begin
tff:
    process (S, clk, t_tmp)
    begin
        if S = '0' then
            t_tmp <= '1';
        elsif  rising_edge(clk) then
            t_tmp <= T xor t_tmp;
        end if;
        Q <= t_tmp;
    end process;
end architecture;

library ieee;
use ieee.std_logic_1164.all;

entity test_tff is
end entity;

architecture foo of test_tff is
    signal CLK: std_logic := '0';
    signal T:   std_logic := '0';
    signal S:   std_logic := '0';
    signal Q:   std_logic;

    component t_ff_s is
        port (
            signal CLK:     in  std_logic;
            signal T:       in  std_logic;
            signal S:       in  std_logic;
            signal Q:       out std_logic
        );
    end component;
begin

DUT: 
    t_ff_s
        port map (
            T => T,
            S => S,
            CLK => CLK,
            Q => Q
        );
CLOCK:
    process
    begin
        wait for 10 ns;
        CLK <= not CLK;
        if Now > 250 ns then
            wait;
        end if;
    end process;

SET:
    process
    begin
        S <= '0';
        wait for 20 ns;
        S <= '1';
        wait;
    end process;

TOGGLE:
    process
    begin
        wait for 20 ns;
        T <= '1';
        wait for 60 ns;
        T <= '0';
        wait for 40 ns;
        T <= '1';
        wait;
    end process;

end architecture;

configuration my_t_ff_s_config of test_tff is
    for foo 
        for  DUT: t_ff_s
            use entity work.t_ff_s(my_t_ff_s);
        end for;
    end for;
end configuration;

configuration concurrent_config of test_tff is
    for foo 
        for  DUT: t_ff_s
            use entity work.t_ff_s(foe);
        end for;
    end for;
end configuration;

configuration variable_config of test_tff is
    for foo 
        for  DUT: t_ff_s
            use entity work.t_ff_s(fie);
        end for;
    end for;
end configuration;

configuration sensitivity_config of test_tff is
    for foo 
        for  DUT: t_ff_s
            use entity work.t_ff_s(fee);
        end for;
    end for;
end configuration;

note the use of configuration

Using VHDL's configuration declarations to allow the use of multiple architectures. (my_t_ff_s - the original, foe - with concurrent assignment to Q, fie - with t_tmp as a variable and fee - with t_tmp in the sensitivity list).

And amazingly enough ghdl's analyzer was quite helpful getting the configuration declarations syntax right. Once you get the first one, the others are easy.

We tend to get rusty using configuration, it wasn't generally supported historically by synthesis tools. But then again, this is simulation for verification.

And for those with ghdl and gtkwave this is how it was done:

ghdl -a t_ff.vhdl
ghdl -e my_t_ff_s_config
ghdl -e concurrent_config
ghdl -e concurrent_config
ghdl -e sensitivity_config
ghdl -r my_t_ff_s_config --wave=test_tff_my_t_ff_s.ghw
ghdl -r concurrent_config --wave=test_tff_foe.ghw
ghdl -r variable_config --wave=test_tff_fie.ghw
ghdl -r sensitivity_config --wave=test_tff_fee.ghw

GHW is ghdl's native waveform dump file format, understood by gtkwave.

In gtkwave:

open t_ff_s.gtkw (reads in test_tff_my_t_ff_s.ghw)
(otherwise read in test_tff_my_t_ff_s.ghw and add signals to
waveform display, format the window, save save file to t_ff_s.gtkw)

new tab open test_tff_foe.ghw
read save file open t_ff_s.gtkw
new tab open test_tff_fie.ghw
read save file open t_ff_s.gtkw
new tab open test_tff_fee.ghw
read save file open t_ff_s.gtkw

Note ghdl doesn't save variable state or delta cycles, t_tmp won't show up in the waveform for test_ff_fie.ghw.

Upvotes: 2

Kevin Thibedeau
Kevin Thibedeau

Reputation: 3411

Sensitivity lists in VHDL don't take an edge specification like in Verilog. VHDL is more flexible in that you can freely use the 'event signal attribute anywhere within a process to implement edge triggered behavior. You can mix level and edge sensitive logic without resorting to split blocks/processes or hacks like negedge for resets. Function calls like rising_edge(clk) (which implements a test for clk'event) are not permitted in a sensitivity list. It only contains signal names. Your code won't compile as is.

If some other syntactically correct version of your code compiles cleanly, the delays you see are artifacts of the simulation model or having a broken sensitivity list. If you want a synchronous clock driven process then you only need the clock signal and possibly an asynchronous reset in the sensitivity list.

Consider the following process:

tff: process(S, clk)
begin
  if S = '0' then -- Asynchronous reset (level sensitive)
    t_tmp <= '1';
  elsif rising_edge(clk) then -- Synchronous logic (edge sensitive)
    t_tmp <= T xor t_tmp;
  end if;
end process;

Q <= t_tmp;

This process executes when an event occurs on S or clk. If S is '0' then the reset condition is executed with priority over the elsif clause (clk is a don't-care). The assignment to t_tmp takes effect on the next delta cycle which is still the same as the current simulation time. Otherwise, if rising_edge(clk) evaluates to true then an event occurred on clk and it's state changed from '0' (or 'L') to '1' (or 'H') indicating that the event was a rising edge. The synchronous assignment takes place and the new xored t_tmp takes effect on the next delta cycle. Changes in T don't cause the process to execute since it isn't (and shouldn't be) in the sensitivity list.

Because there is no unconditional else clause the t_tmp signal retains its last assigned value if both of the two if conditions are false. It will change the next time there is an event on S or clk that causes a new assignment to t_tmp. This will either be the next clock edge or a re-application of asynchronous reset.

The assignment to Q is continuous and is effectively the same as a process with t_tmp in its sensitivity list. As a consequence, the assignment to Q takes place a delta cycle after events on t_tmp which is two delta cycles after the rising edge. If Q is feeding into logic that updates earlier than the second delta cycle of an edge, it will appear to take an extra clock cycle for it to propagate.

The behavior surrounding delta cycles can sometimes create confusing results when inspecting waveforms. You may have a rising edge that should capture a data input that appears to transition simultaneously on the same time step when, in fact, the data is transitioning on a later delta cycle and will only be captured on the next clock edge.

Similarly, if you construct a simple gated clock without any time delay, its edges will occur at the same time but on later delta cycles than the ungated version of the clock. Data driven from the "earlier" ungated clock will be captured by the gated logic a clock cycle earlier than expected as a result. Data driven the other direction will appear to have an unexpected delay by a clock cycle.

It isn't clear what is causing the problem you see without more information on how you're driving the S, T, and clk signals but it is likely connected to the delta cycle behavior of the simulation engine in some way.

Upvotes: 4

Related Questions