Reputation: 955
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:
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
Reputation: 11261
The code does exactly what it describes:
at a rising edge of clk
:
shift_reg(0)
to shift_reg(1)
and F0_temp
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
:
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;
A VHDL simulator operates in two alternating phases:
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