Encoder Debounce VHDL - vhdl

For practice, I attempted to make a VHDL code to run Rotary encoder hardware. It was full with debounce, quadrature decoder and an up/down counter codes.
Unfortunately, when running simulation with a testbench, my results were disappointing so I decided that each block needs to be analysed separately. For the debounce code, I created a symbol file and produced the circuit below
(clickable)
While attempting to debounce the testbench signal in simulation, I found out that my results were terrible so I believe I am missing something.
(clickable)
The VHDL code for the encoder block can be found here :
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;
ENTITY debounce_a IS
GENERIC( counter_size : INTEGER := 8); --counter size (8 bits gives 51.6 us with 5MHz clock)
PORT(
clk : IN STD_LOGIC; --input clock
bounce_a : IN STD_LOGIC; --input signal to be debounced
result : OUT STD_LOGIC); --debounced signal
END debounce_a;
ARCHITECTURE logic OF debounce_a IS
SIGNAL flipflops : STD_LOGIC_VECTOR(1 DOWNTO 0); --input flip flops
SIGNAL counter_set : STD_LOGIC; --sync reset to zero
SIGNAL counter_out : STD_LOGIC_VECTOR(counter_size DOWNTO 0) := (OTHERS => '0'); --counter output
BEGIN
counter_set <= flipflops(0) xor flipflops(1); --determine when to start/reset counter
PROCESS(clk) -- occurs within a clock event
BEGIN
IF(clk'EVENT and clk = '1') THEN -- can be rising_edge(clk)
flipflops(0) <= bounce_a; -- adresses the signal as a set value
flipflops(1) <= flipflops(0);
If(counter_set = '1') THEN --reset counter because input is changing
counter_out <= (OTHERS => '0'); --set all bits to '0'
ELSIF(counter_out(counter_size) = '0') THEN --stable input time is not yet met
counter_out <= counter_out + 1;
ELSE --stable input time is met
result <= flipflops(1);
END IF;
END IF;
END PROCESS;
END logic;
It could be filled to the brim with unwanted logic because I had a limited idea of what I was doing and I took a friends general debounce code and edited it to try and fit my specific needs.
If anything extra is needed I am willing to provide. I feel like it's a pretty basic error but I just need any sort of help.
Here is a look on the testbench code that I used if that helps identify toggling properties :
LIBRARY ieee;
USE ieee.std_logic_1164.all;
ENTITY decodeblock_vhd_tst IS
END decodeblock_vhd_tst;
ARCHITECTURE decodeblock_arch OF decodeblock_vhd_tst IS
-- constants
CONSTANT clk_period : TIME := 20 ns;
CONSTANT num_clk_cycles : INTEGER := 100;
-- signals
SIGNAL b_Input : STD_LOGIC;
SIGNAL b_output : STD_LOGIC;
SIGNAL CLOCK_50 : STD_LOGIC := '0';
COMPONENT decodeblock
PORT (
b_Input : IN STD_LOGIC;
b_output : OUT STD_LOGIC;
CLOCK_50 : IN STD_LOGIC
);
END COMPONENT;
BEGIN
i1 : decodeblock
PORT MAP (
-- list connections between master ports and signals
b_Input => b_Input,
b_output => b_output,
CLOCK_50 => CLOCK_50
);
init : PROCESS
-- variable declarations
BEGIN
b_input <= '0',
'1' after 1.1 ns,
'0' after 2.9 ns,
'1' after 5.1 ns,
'0' after 7.6 ns,
'1' after 9.9 ns,
'0' after 12.5 ns,
'1' after 15.4 ns,
'0' after 18.6 ns,
'1' after 22.1 ns,
'0' after 25.9 ns,
'1' after 29.7 ns,
'0' after 33.8 ns,
'1' after 38.2 ns;
-- variable declarations
for i in 1 to num_clk_cycles loop
CLOCK_50 <= not CLOCK_50;
wait for clk_period/2;
CLOCK_50 <= not CLOCK_50;
wait for clk_period/2;
end loop;
WAIT;
END PROCESS init;
always : PROCESS
-- optional sensitivity list
-- ( )
-- variable declarations
BEGIN
-- code executes for every event on sensitivity list
WAIT;
END PROCESS always;
END decodeblock_arch;

Related

VHDL counter simulated using a test bench giving 'Uninitialized' for the output, how is this resolved?

Below is a counter that is designed to represent an 8 bit binary number with 8 LEDs, it is being simulated using a test bench, however when running the simulation the output simply shows UU for the led.
Here is the main entity that I wish to test:
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_unsigned.all;
entity Lab_3_Source_File is
generic(N_BITS : integer := 8);
port(
btnd : in STD_LOGIC ;
clk : in STD_LOGIC;
led : out STD_LOGIC_VECTOR(7 downto 0)
);
end Lab_3_Source_File;
architecture counter of Lab_3_Source_File is
signal count: STD_LOGIC_VECTOR(7 downto 0);
begin
process(clk, btnd)
begin
if btnd = '1' then
count <= (others => '0');
elsif rising_edge(clk) then
count <= count + 1;
end if;
end process;
led <= count;
end counter;
Here is the test bench that I have tried to map to the main entity:
use IEEE.STD_LOGIC_1164.ALL;
entity Count_TestBench is
end Count_TestBench;
architecture Behavioral of Count_TestBench is
signal btnd, clk : STD_LOGIC;
signal led : STD_LOGIC_VECTOR(7 downto 0);
begin
UUT : entity work.Lab_3_Source_File port map (btnd => btnd,clk => clk,led => led);
process
begin
btnd<='1';
wait for 1 ns;
btnd<='0';
led<= (others => '0');
for i in 1 to 100 loop
clk<='1';
wait for 10 ns;
clk<='0';
wait for 10 ns;
led<=led;
end loop;
end process;
end Behavioral;
Please could somebody help me understand how to enable the simulation to display the led output incrementing?
EDIT:
Set btnd to 1 with a 1ns wait in the test bench to initialise the led, following the answer from mkrieger1, the led output is still at U following this change.
count is not initialized inside Lab_3_Source_File until btnd is set to '1', which it isn't in the testbench.
Since the led output is driven by count, it is also uninitialized. The uninitialized value of the led output of Lab_3_Source_File is then assigned to the led signal in the testbench.
So, to fix this, you need to set btnd to '1' once for a non-zero duration in the testbench, before setting it to '0' again (otherwise led is held at "00000000" constantly).

VHDL: Button debouncing (or not, as the case may be)

I've read through the other posts but can't seem to fix mine. I'm new to VHDL so I'm sure it's a simple fix.
In short, the button isn't debouncing. The code compiles and the bitstream programs. In the testbench, button presses work, but the output LEDs don't change. On the board, pressing a button makes random LEDs light up (I presume because of bouncing). According to the schematic the inputs are going through the debouncers.
Can anyone identify the issue? And any other hints and tips are always appreciated :)
Thanks!
EDIT1: Added rising_edge(clk).
Also note, when I press either button, at the time it's depressed all the LEDs light up.
button_counter.vhd
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity button_counter is
port( clk : in std_logic;
btnU : in std_logic;
btnD : in std_logic;
led : out std_logic_vector (15 downto 0));
end button_counter;
architecture behavioral of button_counter is
component debouncer is
port( clk : in std_logic;
btn : in std_logic;
btn_clr : out std_logic);
end component;
signal btnU_clr : std_logic;
signal btnD_clr : std_logic;
begin
debouncer_btnU : debouncer port map (clk => clk, btn => btnU, btn_clr => btnU_clr);
debouncer_btnD : debouncer port map (clk => clk, btn => btnD, btn_clr => btnD_clr);
process(clk)
variable count : integer := 0;
begin
if (rising_edge(clk)) then
if(btnU_clr = '1') then count := count + 1;
elsif(btnD_clr = '1') then count := count - 1;
end if;
led <= std_logic_vector(to_unsigned(count, led'length));
end if;
end process;
end behavioral;
Debouncer.vhd
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity debouncer is
port( clk : in std_logic;
btn : in std_logic;
btn_clr : out std_logic);
end debouncer;
architecture behavioural of debouncer is
constant delay : integer := 650000; -- 6.5ms
signal count : integer := 0;
signal btn_tmp : std_logic := '0';
begin
process(clk)
begin
if rising_edge(clk) then
if (btn /= btn_tmp) then
btn_tmp <= btn;
count <= 0;
elsif (count = delay) then
btn_clr <= btn_tmp;
else
count <= count + 1;
end if;
end if;
end process;
end behavioural;
button_counter_tb.vhd
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity button_counter_tb is
end button_counter_tb;
architecture behavioral of button_counter_tb is
signal clk_tb : std_logic;
signal btnU_tb : std_logic;
signal btnD_tb : std_logic;
signal led_tb : std_logic_vector (15 downto 0);
component button_counter
port(clk : in std_logic;
btnU : in std_logic;
btnD : in std_logic;
led : out std_logic_vector (15 downto 0));
end component;
begin
UUT: button_counter port map (clk => clk_tb, btnU => btnU_tb, btnD => btnD_tb, led => led_tb);
process
begin
btnU_tb <= '0';
btnD_tb <= '0';
wait for 100ns;
btnU_tb <= '1';
wait for 100ns;
btnU_tb <= '0';
wait for 100ns;
btnU_tb <= '1';
wait for 100ns;
btnD_tb <= '1';
wait for 100ns;
btnU_tb <= '0';
wait for 100ns;
btnD_tb <= '0';
end process;
end behavioral;
After your code update there are several issues remaining:
The clock isn't being generated in the testbench
The stimuli (button presses) aren't adequately timed in the testbench
The debouncer doesn't produce an enable for a single clock
To facilitate simulation for design validation your design has been modified to allow a slower clock (it appears you're actually using a 100 MHz clock). The idea is to reduce the computation requirements and display waveform storage.
The first two points are addressed in the testbench:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity button_counter_tb is
end entity button_counter_tb;
architecture behavioral of button_counter_tb is
-- NOTE: suffix _tb has been removed, it's annoying to type over and over
signal clk: std_logic := '0'; -- ADDED default value '0'
signal btnU: std_logic;
signal btnD: std_logic;
signal led: std_logic_vector (15 downto 0);
component button_counter
generic ( -- ADDED generic
CLKP: time := 10 ns;
DEBT: time := 6.5 ms -- debounce time supports different
); -- mechanical buttons/switches
port (
clk: in std_logic;
btnU: in std_logic;
btnD: in std_logic;
led: out std_logic_vector (15 downto 0)
);
end component;
constant CLKP: time := 12.5 us; -- ADDED just long enough to show debounce
constant DEBT: time := 6.5 ms; -- ADDED
begin
CLOCK: -- ADDED clock process
process
begin
wait for CLKP/2;
clk <= not clk;
if now > 2 sec then -- stop simulation
wait;
end if;
end process;
UUT:
button_counter
generic map ( -- ADDED generic map
CLKP => CLKP,
DEBT => DEBT
)
port map (
clk => clk,
btnU => btnU,
btnD => btnD,
led => led
);
-- STIMULI:
-- process
-- begin
-- btnU_tb <= '0';
-- btnD_tb <= '0';
-- wait for 100 ns;
-- btnU_tb <= '1';
-- wait for 100 ns;
-- btnU_tb <= '0';
-- wait for 100 ns;
-- btnU_tb <= '1';
-- wait for 100 ns;
-- btnD_tb <= '1';
-- wait for 100 ns;
-- btnU_tb <= '0';
-- wait for 100 ns;
-- btnD_tb <= '0';
-- wait; -- ADDED -- stops simulation
-- end process;
UP_BUTTON:
process
begin
btnU <= '0';
wait for 2 ms;
btnU <= '1'; -- first button press
wait for 0.5 ms;
btnU <= '0';
wait for 0.25 ms;
btnU <= '1';
wait for 7 ms;
btnU <= '0';
wait for 100 us;
btnU <= '1';
wait for 20 us;
btnU <= '0';
wait for 200 ms;
btnU <= '1'; -- second button press
wait for 20 us;
btnU <= '0';
wait for 20 us;
btnU <= '1';
wait for 6.6 ms;
btnU <= '0';
wait for 250 ms;
btnU <= '1'; -- third button press
wait for 20 us;
btnU <= '0';
wait for 20 us;
btnU <= '1';
wait for 6.6 ms;
btnU <= '0';
wait for 200 ms;
btnU <= '1'; -- second button press
wait for 20 us;
btnU <= '0';
wait for 20 us;
btnU <= '1';
wait for 6.6 ms;
btnU <= '0';
wait for 50 us;
btnU <= '1';
wait for 1 ms;
btnU <= '0';
wait;
end process;
DOWN_BUTTON:
process
begin
btnD <= '0';
wait for 800 ms;
btnD <= '1'; -- first button press
wait for 0.5 ms;
btnD <= '0';
wait for 0.25 ms;
btnD <= '1';
wait for 0.5 ms;
btnD <= '0';
wait for 1 ms;
btnD <= '1';
wait for 7 ms;
btnD <= '0';
wait for 100 us;
btnD <= '1';
wait for 20 us;
btnD <= '0';
wait for 200 ms;
btnD <= '1'; -- second button press
wait for 20 us;
btnD <= '0';
wait for 20 us;
btnD <= '1';
wait for 6.6 ms;
btnD <= '0';
wait for 250 ms;
wait;
end process;
end architecture behavioral;
The _tb suffix to signal names has been removed (it was painful to type in repetitively).
A clock period has been picked with a ratio of bounce period to clk period guaranteed to allow dropping 'bounces'. The stimului button presses can be extended as can the simulation which is arbitrary here.
Note the button press values are guaranteed to span one or more clock intervals.
These should tolerate the clock period being changed by modifying CLKP.
The debounce interval DEBT can be modified to reflect the use of different switches or buttons, including membrane switches with severe aging. The debounce interval is a consequence of mechanical characteristics of the particular switches or buttons. Passing these generic constants allows a degree of platform independence.
The third point is addressed by changes to the debouncer:
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity debouncer is
generic ( -- ADDED GENERICS to speed up simulation
CLKP: time := 10 ns;
DEBT: time := 6.5 ms
);
port (
clk: in std_logic;
btn: in std_logic;
btn_clr: out std_logic
);
end entity debouncer;
architecture behavioural of debouncer is
-- constant delay: integer := 650000; -- 6.5ms
constant DELAY: integer := DEBT/CLKP;
signal count: integer := 0;
signal b_enab: std_logic := '0'; -- RENAMED, WAS btn_tmp
signal btnd0: std_logic; -- ADDED for clock domain crossing
signal btnd1: std_logic; -- DITTO
begin
CLK_DOMAIN_CROSS: -- ADDED process
process (clk)
begin
if rising_edge(clk) then
btnd0 <= btn;
btnd1 <= btnd0;
end if;
end process;
DEBOUNCE_COUNTER: -- ADDED LABEL
process (clk)
begin
if rising_edge(clk) then
-- if btn /= btn_tmp then -- REWRITTEN
-- btn_tmp <= btn;
-- count <= 0;
-- elsif count = DELAY then
-- btn_clr <= btn_tmp;
-- else
-- count <= count + 1;
-- end if;
btn_clr <= '0'; -- btn_clr for only one clock, used as enable
if btnd1 = '0' then -- test for btn inactive state
count <= 0;
elsif count < DELAY then -- while btn remains in active state
count <= count + 1;
end if;
if count = DELAY - 1 then -- why btn_clr '1' or 1 clock
btn_clr <= '1';
end if;
end if;
end process;
end architecture behavioural;
The debouncer has been modified to get a clock domain button value which is used to reset and enable the counter count. The output btn_clr name has been left intact and is true for only one clock and can be used as an enable.
CLKP and DEBT are used together to allow faster simulation execution while passing the same simulation time.
Note the active state of the button input is hard coded. These would be connected to device pins where the input polarity can be specified.
Modifications to button_counter pass generic constants CLKP and DEBT to the debouncers:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity button_counter is
generic (
CLKP: time := 10 ns; -- GENERIC CONSTANTS for faster simulation
DEBT: time := 6.5 ms -- supports diffeent switches/buttons
);
port (
clk: in std_logic;
btnU: in std_logic;
btnD: in std_logic;
led: out std_logic_vector (15 downto 0)
);
end entity button_counter;
architecture behavioral of button_counter is
component debouncer is
generic (
CLKP: time := 10 ns;
DEBT: time := 6.5 ms
);
port (
clk: in std_logic;
btn: in std_logic;
btn_clr: out std_logic
);
end component;
signal btnU_clr: std_logic;
signal btnD_clr: std_logic;
begin
debouncer_btnU:
debouncer
generic map (
CLKP => CLKP,
DEBT => DEBT
)
port map (
clk => clk,
btn => btnU,
btn_clr => btnU_clr
);
debouncer_btnD:
debouncer
generic map (
CLKP => CLKP,
DEBT => DEBT
)
port map (
clk => clk,
btn => btnD,
btn_clr => btnD_clr
);
process (clk)
variable count: integer := 0;
begin
if rising_edge(clk) then
if btnU_clr = '1' then
count := count + 1;
elsif btnD_clr = '1'then
count := count - 1;
end if;
led <= std_logic_vector(to_unsigned(count, led'length));
end if;
end process;
end architecture behavioral;
And when simulated we now see the LEDs count up and down:
Running the testbench and displaying the various waveforms would allow 'zooming in' to display glitch handling in the two debouncers.
The modifications to pass the clock period and debounce interval through the design hierarchy wouldn't be strictly essential. They facilitate simulation which is used as here for design validation. (The stimuli shown in the testbench don't exhaustively verify the design).
By using the generic defaults (with a 100MHz clock) there's a very good chance the design will function when implemented in a target platform. (The active polarity of button inputs is selected in the debouncer to support the original implementation. if you suspect button bounces while getting increments or decrements you can increase the DEBT value.)
If a particular synthesis tool can't handle value of type time passed as generic constants you can convert the various declarations of CLKP and DEBT to type integer or simply pass the maximum count.
You forget the rising_edge in your button_counter.vhd.
process(clk)
variable count : integer := 0;
begin
if(btnU_clr = '1') then count := count + 1;
elsif(btnD_clr = '1') then count := count - 1;
end if;
led <= std_logic_vector(to_unsigned(count, led'length));
end process;
So fix this and maybe it works (I don´t test the design, because of this obvious error):
process(clk)
variable count : integer := 0;
begin
if(rising_edge(clk)) then
...
end if;
end process;
I´m not sure, but I think the toolchain will produce some warnings for this. So check it please.
And your Testbench doesn´t contain any clock generation process, so you will not have a clock signal. Maybe this will let you believe that your design works (or did you forget the clock clk_tb signal in your post?).
The question has been answered well, but I would like to highlight different techniques for synchronising and debouncing.
Synchronising
For synchronising, a simple buffer or chain can be used which avoids creating separate signals/variables for each stage in the buffer or chain. A generic constant can be used to control the length of the chain (minimum of 2):
signal sync_buffer: std_logic_vector(SYNC_BUFFER_MSB downto 0); -- N-bit synchronisation buffer.
...
sync_buffer <= sync_buffer(SYNC_BUFFER_MSB - 1 downto 0) & input;
Debouncing
For debouncing, hysteresis (a fancy word for history or memory) can be used to create a kind of low pass filter that will debounce both the press and release of a button, and detect edges (both positive and negative) irrespective of whether the signal is active high or active low. The output will stay in its current state until the synchronised input remains in the opposite state for N consecutive clock cycles:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity Debounce is
generic
(
CLOCK_PERIOD : time := 20 ns;
DEBOUNCE_PERIOD: time := 125 ms; -- 1/8th second as a rule of thumb for a tactile button/switch.
SYNC_BITS : positive := 3 -- Number of bits in the synchronisation buffer (2 minimum).
);
port
(
clock : in std_logic;
input : in std_logic; -- Asynchronous and noisy input.
output: out std_logic := '0'; -- Synchronised, debounced and filtered output.
edge : out std_logic := '0'; -- Goes high for 1 clock cycle on either edge of synchronised and debounced input.
rise : out std_logic := '0'; -- Goes high for 1 clock cycle on the rising edge of synchronised and debounced input.
fall : out std_logic := '0' -- Goes high for 1 clock cycle on the falling edge of synchronised and debounced input.
);
end entity;
architecture V1 of Debounce is
constant SYNC_BUFFER_MSB: positive := SYNC_BITS - 1;
signal sync_buffer: std_logic_vector(SYNC_BUFFER_MSB downto 0) := (others => '0'); -- N-bit synchronisation buffer (2 bits minimum).
alias sync_input: std_logic is sync_buffer(SYNC_BUFFER_MSB); -- The synchronised input is the MSB of the synchronisation buffer.
constant MAX_COUNT: natural := DEBOUNCE_PERIOD / CLOCK_PERIOD;
signal counter: natural range 0 to MAX_COUNT := 0; -- Specify the range to reduce number of bits that are synthesised.
begin
assert SYNC_BITS >= 2 report "Need a minimum of 2 bits in the synchronisation buffer.";
process(clock)
variable edge_internal: std_logic := '0';
variable rise_internal: std_logic := '0';
variable fall_internal: std_logic := '0';
begin
if rising_edge(clock) then
-- Synchronise the asynchronous input.
-- MSB of sync_buffer is the synchronised input.
sync_buffer <= sync_buffer(SYNC_BUFFER_MSB - 1 downto 0) & input;
edge <= '0'; -- Goes high for 1 clock cycle on either edge.
rise <= '0'; -- Goes high for 1 clock cycle on the rising edge.
fall <= '0'; -- Goes high for 1 clock cycle on the falling edge.
if counter = MAX_COUNT - 1 then -- If successfully debounced, notify what happened, and reset the counter.
output <= sync_input;
edge <= edge_internal; -- Goes high for 1 clock cycle on either edge.
rise <= rise_internal; -- Goes high for 1 clock cycle on the rising edge.
fall <= fall_internal; -- Goes high for 1 clock cycle on the falling edge.
counter <= 0;
elsif sync_input /= output then
counter <= counter + 1;
else
counter <= 0;
end if;
end if;
-- Edge detection.
edge_internal := sync_input xor output;
rise_internal := sync_input and not output;
fall_internal := not sync_input and output;
end process;
end architecture;
Button Counter
Much the same as the other answers, but I've used the rise outputs of the debouncers to trigger the counting. I also added a couple of LEDs for visual button feedback.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity ButtonCounter is
generic
(
CLOCK_PERIOD : time := 20 ns;
DEBOUNCE_PERIOD: time := 125 ms
);
port
(
clock : in std_logic;
btn_up: in std_logic;
btn_dn: in std_logic;
led_up: out std_logic;
led_dn: out std_logic;
leds : out std_logic_vector(15 downto 0)
);
end entity;
architecture V1 of ButtonCounter is
signal count_up: std_logic;
signal count_dn: std_logic;
component Debounce is
generic
(
CLOCK_PERIOD : time := 20 ns;
DEBOUNCE_PERIOD: time := 125 ms
);
port
(
clock : in std_logic;
input : in std_logic;
output: out std_logic;
rise : out std_logic
);
end component;
begin
DEBOUNCE_BTN_UP:
Debounce
generic map
(
CLOCK_PERIOD => CLOCK_PERIOD,
DEBOUNCE_PERIOD => DEBOUNCE_PERIOD
)
port map
(
clock => clock,
input => btn_up,
output => led_up,
rise => count_up -- Goes high for 1 clock cycle on the rising edge of btn_up.
);
DEBOUNCE_BTN_DN:
Debounce
generic map
(
CLOCK_PERIOD => CLOCK_PERIOD,
DEBOUNCE_PERIOD => DEBOUNCE_PERIOD
)
port map
(
clock => clock,
input => btn_dn,
output => led_dn,
rise => count_dn -- Goes high for 1 clock cycle on the rising edge of btn_dn.
);
process(clock)
variable counter: natural range 0 to 2 ** leds'length - 1 := 0; -- Specify the range to reduce number of bits that are synthesised.
begin
if rising_edge(clock) then
if count_up then
counter := counter + 1;
elsif count_dn then
counter := counter - 1;
end if;
leds <= std_logic_vector(to_unsigned(counter, leds'length));
end if;
end process;
end architecture;
Test Bench
Some asynchronous and noisy input buttons are synchronised, debounced and filtered. The positive edges of the reformed input signals trigger the counting.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
entity ButtonCounter_TB is
end;
architecture V1 of ButtonCounter_TB is
constant CLOCK_PERIOD : time := 50 ns;
constant DEBOUNCE_PERIOD: time := 200 ns;
signal halt_sys_clock: boolean := false;
signal clock: std_logic := '0';
signal btn_up: std_logic;
signal btn_dn: std_logic;
signal leds: std_logic_vector(15 downto 0);
component ButtonCounter is
generic
(
CLOCK_PERIOD : time := 10 ns;
DEBOUNCE_PERIOD: time := 125 ms
);
port
(
clock : in std_logic;
btn_up: in std_logic;
btn_dn: in std_logic;
leds : out std_logic_vector(15 downto 0)
);
end component;
begin
ClockGenerator:
process
begin
while not halt_sys_clock loop
clock <= not clock;
wait for CLOCK_PERIOD / 2.0;
end loop;
wait;
end process ClockGenerator;
Stimulus:
process
constant NUM_NOISE_SAMPLES: positive := 10;
constant SWITCH_TIME: time := 2 * DEBOUNCE_PERIOD;
variable seed1: positive := 1;
variable seed2: positive := 1;
variable rrand: real;
variable nrand: natural;
-- Performs noisy transition of sig from current value to final value.
procedure NoisyTransition(signal sig: out std_logic; final: std_logic) is
begin
for n in 1 to NUM_NOISE_SAMPLES loop
uniform(seed1, seed2, rrand);
nrand := natural(round(rrand));
if nrand = 0 then
sig <= not final;
else
sig <= final;
end if;
wait for CLOCK_PERIOD / 5.0;
end loop;
sig <= final;
wait for SWITCH_TIME;
end;
begin
btn_up <= '0';
btn_dn <= '0';
wait for 3 ns;
--
-- Up Button
--
-- Perform 4 noisy presses and releases.
for n in 1 to 4 loop
NoisyTransition(btn_up, '1');
NoisyTransition(btn_up, '0');
end loop;
--
-- Down Button
--
-- Perform 1 noisy press and release.
NoisyTransition(btn_dn, '1');
NoisyTransition(btn_dn, '0');
halt_sys_clock <= true;
wait;
end process;
DUT:
ButtonCounter
generic map
(
CLOCK_PERIOD => CLOCK_PERIOD,
DEBOUNCE_PERIOD => DEBOUNCE_PERIOD
)
port map
(
clock => clock,
btn_up => btn_up,
btn_dn => btn_dn,
leds => leds
);
end architecture;
Simulation

ISim shows U for all outputs

I have a simple VHDL design and test bench that does not produce the expected output. ISim shows 'U' for all the outputs until the 'running' state is achieved (myState='1'). Then they show 0 and X values. The first PROCESS block should set all outputs to '0' when ENABLE is '0'. The test bench toggles ENABLE 0-1-0 to insure an event triggers the process, but the outputs stay at 'U'. Is the problem in the design, the test, or both?
VHDL
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity TestHarness1 is
port (
ADAT_WDCLK : in std_logic;
ADAT_BCLK: in std_logic;
ADAT_OUT12: in std_logic;
ENABLE: in std_logic;
PCM_FS : out std_logic;
PCM_CLK : out std_logic;
PCM_DIN : out std_logic
);
end TestHarness1;
architecture Behavioral of TestHarness1 is
--type state is (STOPPED, RUNNING);
signal tmp : std_logic;
signal myState : std_logic;
begin
PCM_DIN <= tmp;
-- State management process
process (ENABLE, ADAT_WDCLK) begin -- Eval on input changes
if (ENABLE = '0') then
myState <= '0'; --STOPPED;
PCM_FS <= '0'; -- All outputs muted
PCM_CLK <= '0';
tmp <= '0';
else
if (myState = '0' and rising_edge(ADAT_WDCLK)) then
-- Move to running state only at start of a frame
myState <= '1'; --RUNNING;
end if;
end if;
end process;
-- Output process
process (ADAT_WDCLK, ADAT_BCLK, myState) variable counter: integer := 0; begin
-- Only do something if we are in running state, process above
-- sets outputs when stopped.
if (myState = '1') then
-- Pass the clocks through, inverting the bit clock
PCM_FS <= ADAT_WDCLK;
PCM_CLK <= not ADAT_BCLK;
-- Generate fixed bit pattern data '11000101'
if rising_edge(ADAT_WDCLK) then
-- This would happen naturally since there are 4 bytes per word clock
counter := 0;
end if;
if falling_edge(ADAT_WDCLK) then
-- This would happen naturally since there are 4 bytes per word clock
counter := 0;
end if;
if rising_edge(ADAT_BCLK) then -- Change data state only on falling edge of output PCM_CLK
if counter = 0 or counter = 1 or counter = 5 or counter = 7 then
tmp <= '1';
else
tmp <= '0';
end if;
if (counter = 7) then
counter := 0; -- Reset counter
else
counter := counter + 1; -- Just inc counter
end if;
end if;
end if;
end process;
end Behavioral;
Test Bench
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY TH1TestBench3 IS
END TH1TestBench3;
ARCHITECTURE behavior OF TH1TestBench3 IS
-- Component Declaration for the Unit Under Test (UUT)
COMPONENT TestHarness1
PORT(
ADAT_WDCLK : IN std_logic;
ADAT_BCLK : IN std_logic;
ADAT_OUT12 : IN std_logic;
ENABLE : IN std_logic;
PCM_FS : OUT std_logic;
PCM_CLK : OUT std_logic;
PCM_DIN : OUT std_logic
);
END COMPONENT;
--Inputs
signal ADAT_WDCLK : std_logic := '0';
signal ADAT_BCLK : std_logic := '0';
signal ADAT_OUT12 : std_logic := '0';
signal ENABLE : std_logic := '0';
--Outputs
signal PCM_FS : std_logic;
signal PCM_CLK : std_logic;
signal PCM_DIN : std_logic;
-- Clock period definitions. Note WDCLK is defined in terms of the bit clock
-- to insure they are exactly in sync.
constant ADAT_BCLK_period : time := 326 ns; -- About 3.072MHz (https://www.sensorsone.com/frequency-to-period-calculator/)
constant ADAT_WDCLK_period : time := ADAT_BCLK_period * 64; -- 48KHz
BEGIN
-- Instantiate the Unit Under Test (UUT)
uut: TestHarness1 PORT MAP (
ADAT_WDCLK => ADAT_WDCLK,
ADAT_BCLK => ADAT_BCLK,
ADAT_OUT12 => ADAT_OUT12,
ENABLE => ENABLE,
PCM_FS => PCM_FS,
PCM_CLK => PCM_CLK,
PCM_DIN => PCM_DIN
);
-- Clock process definitions
ADAT_WDCLK_process :process
begin
ADAT_WDCLK <= '0';
wait for ADAT_WDCLK_period/2;
ADAT_WDCLK <= '1';
wait for ADAT_WDCLK_period/2;
end process;
ADAT_BCLK_process :process
begin
ADAT_BCLK <= '1';
wait for ADAT_BCLK_period/2;
ADAT_BCLK <= '0';
wait for ADAT_BCLK_period/2;
end process;
-- Stimulus process
stim_proc: process
begin
-- hold reset state for 100 ns.
wait for 100 ns;
ENABLE <= '1';
wait for 100 ns;
ENABLE <= '0';
wait for 7500 ns;
ENABLE <= '1';
wait for ADAT_WDCLK_period*10;
-- insert stimulus here
wait;
end process;
END;
ISim shows the ENABLE pulse early in the simulation, but the outputs remain 'U' until the rising edge of the WCLK with ENABLE=1. Then they start to change (as designed) but they show some X values.
Modified VHDL
For reference, here is the modified VHDL that resolves the problem of U's and X's in the simulation output. However, there is a functional problem with the PCM_DIN output... seems like it is delayed one (BCLK) cycle. I expected it to be '1' as soon as ADAT_WDCLK goes high the first time after ENABLE. But it does not go to '1' until a BLCK cycle later.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity TestHarness1 is
port (
ADAT_WDCLK : in std_logic;
ADAT_BCLK: in std_logic;
ADAT_OUT12: in std_logic;
ENABLE: in std_logic;
PCM_FS : out std_logic;
PCM_CLK : out std_logic;
PCM_DIN : out std_logic
);
end TestHarness1;
architecture Behavioral of TestHarness1 is
--type state is (STOPPED, RUNNING);
signal tmp : std_logic;
signal myState : std_logic;
begin
PCM_DIN <= tmp;
-- State management process
process (ENABLE, ADAT_WDCLK) begin -- Eval on input changes
if (ENABLE = '0') then
myState <= '0'; --STOPPED;
else
if (myState = '0' and rising_edge(ADAT_WDCLK)) then
-- Move to running state only at start of a frame
myState <= '1'; --RUNNING;
end if;
end if;
end process;
-- Output process
process (ADAT_WDCLK, ADAT_BCLK, myState) variable counter: integer := 0; begin
-- Only do something if we are in running state
if (myState = '0') then
PCM_FS <= '0'; -- All outputs muted
PCM_CLK <= '0';
tmp <= '0';
elsif (myState = '1') then
-- Pass the clocks through, inverting the bit clock
PCM_FS <= ADAT_WDCLK;
PCM_CLK <= not ADAT_BCLK;
if rising_edge(ADAT_BCLK) then -- Generate fixed serial bit pattern
if counter = 0 or counter = 1 or counter = 5 or counter = 7 then
tmp <= '1';
else
tmp <= '0';
end if;
if (counter = 7) then
counter := 0; -- Reset counter
else
counter := counter + 1; -- Just inc counter
end if;
end if;
end if;
end process;
end Behavioral;
ISim of the above (including the internal myState signal)... why is PCM_DIN delayed one BCLK cycle?
Regarding the 'X' (Forcing Unknown) values you are seeing:
You are driving the signals PCM_FS, PCM_CLK and tmp from multiple processes, which results in the simulator being unable to resolve the value being driven. You need to fix this such that they are only being driven from one process, or drive 'Z' when they are not in use.
Regarding the 'U' values, they exist because you have no initial values for the signals. Once you write the signals for the first time (after the enable), they will be assigned for the first time.

VHDL - Non-regular clock pattern generation

My question is relatively simple: is it possible in VHDL to generate a clock with a specific, non-regular pattern without using a process and the after keyword?
In other words, if I have a process that generates a clock like this:
process(Start)
begin
if Start = '0' then
clock <= '0';
else if Start = '1' then
clock <= '0',
'1' after 25 ns,
'0' after 35 ns,
'1' after 50 ns,
'0' after 75 ns,
'1' after 105 ns,
...
end process;
can I get the same output using (for istance) some components, like some sort of delays?
I know that probably it wouldn't be of any use, but my professor asked us to replicate this exercise using structural VHDL. I just need to simulate it, I don't need to make it synthesizable.
Imagine a counter that counts 5 ns period clocks and an initial value for clock of '0' and the counter of (others => '0'). at count = 5 (25 ns), 7 (35 ns), 10 (50 ns) a combinatorial signal toggle goes true ('1'),... with others toggle set to false ('0'). If the clock is continuous you pick a count to restore the count to all '0's. The toggle signal is an enable for a D Flip Flop (clock is the output) with it's Qnot connected to D run off the high speed clock (supplied externally from a testbench). The size of count is determined last even in 5 ns increments.
Well we'd adjust the count comparison values down by one to compensate for the D Flip Flop and it would look something like this structurally:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity counter is -- counts 0 to 31
port (
clk: in std_logic;
count: out std_logic_vector (4 downto 0);
zero: out std_logic
);
end entity;
architecture foo of counter is
signal cnt: unsigned (count'range) := (others => '0'); -- start at zero
constant period: integer := 31; -- 32 is the period of the the sequence
begin
process (clk)
begin
if rising_edge (clk) then
if cnt < period then
cnt <= cnt + 1;
else
cnt <= (others => '0');
end if;
end if;
end process;
count <= std_logic_vector(cnt);
zero <= '1' when cnt = 0 else
'0';
end architecture;
library ieee;
use ieee.std_logic_1164.all;
entity d_flip_flop is -- has enable
port (
resetn: in std_logic;
clk: in std_logic;
en: in std_logic;
d: in std_logic;
q: out std_logic
);
end entity;
architecture foo of d_flip_flop is
signal qn: std_logic := '1';
begin
process (resetn, clk)
begin
if resetn = '0' then
qn <= '1';
elsif rising_edge (clk) and en = '1' then
qn <= not d;
end if;
end process;
q <= not qn;
end architecture;
library ieee;
use ieee.std_logic_1164.all;
entity inv is
port (
a: in std_logic;
o: out std_logic
);
end entity;
architecture foo of inv is
begin
o <= not a;
end architecture;
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity toggle_pla_rom is
port (
count: in std_logic_vector (4 downto 0);
toggle: out std_logic
);
end entity;
architecture foo of toggle_pla_rom is
type rom_array is array (0 to 31) of std_logic; -- fully constrained
constant rom: rom_array := ( 4 | 6 | 9 | 14 | 20 => '1', others => '0');
begin
toggle <= rom(to_integer(unsigned(count)));
end architecture;
library ieee;
use ieee.std_logic_1164.all;
entity clock_gen is
port (
clk: in std_logic;
clock: out std_logic
);
end entity;
architecture foo of clock_gen is
signal toggle: std_logic;
signal resetn: std_logic;
signal din: std_logic;
signal count: std_logic_vector (4 downto 0);
signal q: std_logic;
signal nq: std_logic;
signal zero: std_logic;
begin
CNTR:
entity work.counter
port map (
clk => clk,
count => count,
zero => zero
);
DIN_INV:
entity work.inv
port map (
a => q,
o => nq
);
ZERO_INV:
entity work.inv
port map (
a => zero,
o => resetn
);
DFF:
entity work.d_flip_flop
port map (
resetn => resetn,
clk => clk,
en => toggle,
d => nq,
q => q
);
TOGGLE_LUT:
entity work. toggle_pla_rom
port map (
count => count,
toggle => toggle
);
clock <= q;
end architecture;
library ieee;
use ieee.std_logic_1164.all;
entity clock_gen_tb is
end entity;
architecture foo of clock_gen_tb is
signal clk: std_logic := '1'; -- clk has 5 ns period
signal clock: std_logic;
begin
CLOCKGEN:
entity work.clock_gen
port map (
clk => clk,
clock => clock
);
CLKGEN:
process
begin
wait for 2.5 ns;
clk <= not clk;
if now > 300 ns then
wait;
end if;
end process;
MONITOR:
process
begin
wait for 0 ns;
wait until clock'event;
report "clock = " & std_logic'image(clock);
end process;
end architecture;
The look up table used to determine the count values where a toggle occurs could be replaced with and/ors, etc. for true primitive level description.
The counter wraps, which is easy to cure (it could stop instead for instance). Because you didn't specify any further edge events it's tailored to the size needed for the number of edge events you did specify. The size of the counter and the number of events can be scaled up.
When run the clock_gen_tb testbench reports clock events and shows that it meets your specification:
logarithmic_clock.vhdl:188:9:#0ms:(report note): clock = '0'
logarithmic_clock.vhdl:188:9:#25ns:(report note): clock = '1'
logarithmic_clock.vhdl:188:9:#35ns:(report note): clock = '0'
logarithmic_clock.vhdl:188:9:#50ns:(report note): clock = '1'
logarithmic_clock.vhdl:188:9:#75ns:(report note): clock = '0'
logarithmic_clock.vhdl:188:9:#105ns:(report note): clock = '1'
logarithmic_clock.vhdl:188:9:#160ns:(report note): clock = '0'
logarithmic_clock.vhdl:188:9:#185ns:(report note): clock = '1'
logarithmic_clock.vhdl:188:9:#195ns:(report note): clock = '0'
logarithmic_clock.vhdl:188:9:#210ns:(report note): clock = '1'
logarithmic_clock.vhdl:188:9:#235ns:(report note): clock = '0'
logarithmic_clock.vhdl:188:9:#265ns:(report note): clock = '1'
The first 6 times match your specification.
A waveform display shows the counts, toggles, zero (representing the flip flop reset) and clock:
One thing this example points out is that all VHDL structural models capable of simulation are described behaviorally.
ok, you could try to use others keywords to generate a delay like wait for and loop inside of process block. This is an example:
clock_gen: process
constant duty_cycle : real := 0.40
constant period : time := 100 ns ;
constant clk_high : time := duty_cycle * period ;
begin
loop
clk <= '0';
wait for period - clk_high; -- clock low time
clk <= '1';
wait for clk_high; -- clock high time
end loop;
end process;
end behavioral;
This is a clock with a variable duty cycle and period.
Creating a clock without a process doesn't make any sense (structural VHDL doesn't provide any kind of synchronization), however you can simulate the special clock pattern by applying the right logic to some regular clock signals provided by a testbench for example.
For example consider having an input of 2 clocks (one start at 0, the other at 1), this will give you the possibility to implement a 2 state machine, think of it like a counter from based on the periodicity of your source clock..
You can also have 2 clocks sources where the periodicity of the second clock is twice the first one to get a 2^2 state machine, in this case you are free to start both clocks at low level (0).
This way you can implement the needed pattern without using any process (no behavioral VHDL is used).

Simple VHDL clocked counter simulation confusion

I am currently slightly confused about my simple counter.
It is implemented as follows:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity simple_counter is
port(
DOUT : out std_logic_vector(3 downto 0);
CE : in std_logic;
CLK : in std_logic;
RSTN : in std_logic
);
end simple_counter;
architecture behavioral of simple_counter is
signal temp : unsigned(3 downto 0);
begin
process(CLK)
begin
if RSTN = '0' then
temp <= (others => '0');
elsif(rising_edge(CLK)) then
if CE = '1' then
if std_logic_vector(temp) = (temp'range => '1') then
temp <= (others => '0');
else
temp <= temp + 1;
end if;
end if;
end if;
end process;
DOUT <= std_logic_vector(temp);
end behavioral;
I use the following testbench for simulation:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library std;
use std.textio.all;
use work.tools_pkg.all;
library work;
--! #class tools_tb
--! #brief Test bench for the tools_tb design
entity counter_tb is
generic (
VOID : integer := 0);
port (
void_i : in std_logic);
end entity counter_tb;
--! #brief
--! #details
architecture sim of counter_tb is
-- Clock period definitions
-- Clock, reset and baud rate definitions
constant CLK_FREQ : integer := 100_000_000;
constant clk_period : time := (1.0 / real(CLK_FREQ)) * (1 sec);
signal end_sim : boolean := false;
signal rstn : std_logic;
signal clk : std_logic;
signal s_en : std_logic := '0';
------------------------------------------------------------------------------
-- DUT signals
------------------------------------------------------------------------------
signal s_dout : std_logic_vector(3 downto 0) := (others => '0');
signal s_ce : std_logic := '0';
begin -- architecture
fifo : entity work.simple_counter
port map (
DOUT => s_dout,
CE => s_ce,
RSTN => rstn,
CLK => clk
);
-- Clock process definitions (clock with 50% duty cycle is generated here).
clk_process : process
begin
if end_sim = false then
clk <= '1';
wait for clk_period/2;
clk <= '0';
wait for clk_period/2;
else
wait;
end if;
end process;
-- Stimulus process
stim_proc: process
begin
-- startup and wait for some time
rstn <= '0';
wait for clk_period;
rstn <= '1';
wait for clk_period;
wait for clk_period;
wait for clk_period;
s_ce <= '1';
wait;
end process;
end architecture sim;
I am confused why the counter increases instantly when I set CE <= '1
(see the attached simulation).
Since the counter is implemented in a synchrous process, shouldn't it take a single clock cycle until it is increased from '0' to '1'?
Thanks a lot!
You most likely have a race condition between s_ce and clk. If you will generate the s_ce on the rising edge of clk then you should see that counter works correctly.
I don't know this simulator but to check the race you can expand deltas when counter changes 0->1

Resources