Marco Merlini
Marco Merlini

Reputation: 955

VHDL use input value at clock edge

I have a ring counter which has an enable and a count enable. It so happens that in my larger design, the count enable is synchronous with the clock (and by that I mean the circuit which controls it pulls back to 0 on the rising edge).

Observe:

enter image description here

The F0 and F1 outputs should change at the rising edge happening at t = 130 ns. However, the count_en input gets pulled down at the same time that the ring counter is reading it.

How do I get the correct behaviour from VHDL? Here is my code:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity ring_counter_top is
    Port ( CLK      : in  STD_LOGIC;
              RING_EN   : in  STD_LOGIC;
              COUNT_EN  : in  STD_LOGIC;
           F0           : out STD_LOGIC;
              F1            : OUT STD_LOGIC
             );

end ring_counter_top;

architecture Behavioral of ring_counter_top is
    signal shift_reg : STD_LOGIC_VECTOR(1 downto 0) := "01";
     signal F0_temp : STD_LOGIC := '1';
     signal F1_temp : STD_LOGIC := '0';
     signal count_tmp : std_logic;
begin

     count_tmp <= COUNT_EN;
    -- ring counter
    process (CLK, RING_EN, COUNT_EN)
    begin
     if (RISING_EDGE(CLK)) then
          if (count_tmp = '1') then 
                shift_reg(1) <= shift_reg(0);
                shift_reg(0) <= shift_reg(1);
                F0_temp <= shift_reg(0);
                F1_temp <= shift_reg(1);
          end if;
     end if;
    end process;

     F0 <= F0_temp and RING_EN;
     F1 <= F1_temp and RING_EN;

end Behavioral; 

Upvotes: 1

Views: 2088

Answers (1)

JHBonarius
JHBonarius

Reputation: 11261

The code does exactly what it describes:

at a rising edge of clk:

  • assign the value of shift_reg(0) to shift_reg(1) and F0_temp
  • assign the value of shift_reg(1) to shift_reg(0) and F0_temp

You cannot change the values of a signal within the process (without using time delays, like the wait statements). You can only instruct the simulator to change it at the next delta cycle.

If it is really necessary to change the value within the process, you should use variables instead of signals. But I greatly discourage that, unless you know what you are doing! Variables could pose problems during logic synthesis if used incorrectly.

Just make a small change in your code: switch the initialization values if F1_TEMP and F0_TEMP:

simulation

example code:

library ieee; use ieee.std_logic_1164.all;

entity counter is
    port (
        clk      : in  std_logic;
        count_en : in  std_logic;
        f0       : out std_logic;
        f1       : out std_logic
        );
end entity;

architecture rtl of counter is
    signal shift_reg : std_logic_vector(1 downto 0) := "01";
    signal f0_int : std_logic := '0';
    signal f1_int : std_logic := '1';
begin
    -- ring counter
    process (clk)
    begin
        if rising_edge(clk) then
            if count_en = '1' then 
                shift_reg <= shift_reg(0)&shift_reg(1);
                f0_int <= shift_reg(0);
                f1_int <= shift_reg(1);
            end if;
        end if;
    end process;

    f0 <= f0_int;
    f1 <= f1_int;
end architecture;

library ieee; use ieee.std_logic_1164.all;
entity counter_tb is end entity;
architecture behavioral of counter_tb is
    signal clk, count_en, f0, f1 : std_logic := '0';
begin
    dut: entity work.counter port map(clk, count_en, f0, f1);

    clk <= not clk after 10 ns;

    count_proc: process begin
        count_en <= '0';
        wait for 99 ns;
        wait until rising_edge(clk);
        count_en <= '1';
        wait until rising_edge(clk);
        count_en <= '0';
        wait;
    end process;
end architecture;

Edit:

A VHDL simulator operates in two alternating phases:

  1. statement execution, where triggered statements (such as the process statement) are evaluated and events (like signal assignment) are put in a queue
  2. event processing, where the events in the queue are processed.

Thus if x is a signal and you write x <= y; in a process, then the value of x will not immediately change: it will only be queued. All actual assignments are not processed until the next delta cycle, which will occur after a time delay: In this case after the process is fully evaluated, as there is no wait statement. In testbenches, you can have multiple wait statements in one process, which will induce time delay and hence trigger assignment.

Even more in detail: in application. Say:

entity foo is end entity;
architecture bar of foo is
    signal x : bit := '1';
    signal y : bit;
begin
    process begin
        x <= '0';
        y <= x;
        wait;
    end process;
end architecture;

After simulation end (1-2 delta cycles) x will be '0' and y will be '1', because the assignment of '0' to x will not occur until The next delta cycle, This will occur at the infinite wait statement in this case, because a time delay is introduced.

Upvotes: 1

Related Questions