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).
Related
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).
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
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;
I have been going crazy trying to make it work but nothing been on this for the past 6 hours and still didn't solve it :/
so this the top module
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;
-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity Test is
Port ( CLKI : in STD_LOGIC;
HSO : out STD_LOGIC;
VSO : out STD_LOGIC;
RO,GO,BO : out STD_LOGIC);
end Test;
architecture Behavioral of Test is
component CLK_25Mhz_Divider
Port ( CLK : in STD_LOGIC;
CLK_OUT : out STD_LOGIC);
end component;
component VGA_Sync
Port ( CLK : in STD_LOGIC;
HS : out STD_LOGIC;
VS : out STD_LOGIC;
R,G,B : out STD_LOGIC);
end component;
signal CLKBE: STD_LOGIC;
begin
CLK_Divider_1: CLK_25Mhz_Divider port map ( CLK => CLKI,
CLK_OUT => CLKBE);
VGA_S1: VGA_Sync port map ( CLK => CLKBE,
HS => HSO,
VS => VSO,
R => RO,
G => GO,
B => BO );
end Behavioral;
the clock divider
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;
-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity CLK_25MHz_Divider is
Port ( CLK : in STD_LOGIC;
CLK_OUT : out STD_LOGIC);
end CLK_25MHz_Divider;
architecture Behavioral of CLK_25MHz_Divider is
BEGIN
PROCESS(CLK)
VARIABLE COUNT : INTEGER:=0;
VARIABLE TEMP : STD_LOGIC:='0';
BEGIN
IF RISING_EDGE(CLK)THEN
COUNT:=COUNT+1;
IF COUNT=2 THEN
TEMP:=NOT TEMP;
COUNT:=0;
END IF;
END IF;
CLK_OUT<=TEMP;
END PROCESS;
end Behavioral;
The VGA signal generation module
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;
-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity VGA_Sync is
Port ( CLK : in STD_LOGIC;
HS : out STD_LOGIC;
VS : out STD_LOGIC;
R,G,B : out STD_LOGIC);
end VGA_Sync;
architecture Behavioral of VGA_Sync is
begin
process(CLK)
Variable countH : Integer := 0;
Variable countV : Integer := 0;
begin
if (CLK'EVENT and CLK = '1') then
if countH < 800 then
countH := countH + 1;
else
countH := 0;
if countV < 500 then
countV := countV + 1;
else
countV := 0;
end if;
end if;
if countH >= 16 and countH < 112 then
HS <= '0';
else
HS <= '1';
end if;
if countV >= 10 and countV < 12 then
VS <= '0';
else
VS <= '1';
end if;
if (countH < 160) or (countV < 45) then
R <= '0';
G <= '0';
B <= '0';
else
R <= '1';
G <= '0';
B <= '1';
end if;
end if;
end process;
end Behavioral;
so tell me your thoughts on what is wrong with the code
Because you haven't actually describe the problem and because I had a testbench for a 25 MHz clocked vga generator that only required changing the type for r, g and b, I ran you sync_vga against the testbench:
library ieee;
use ieee.std_logic_1164.all;
entity vga_sync_tb is
end entity;
architecture foo of vga_sync_tb is
signal clk: std_logic := '0';
signal hs: std_logic;
signal vs: std_logic;
signal r,g,b: std_logic;
begin
DUT:
entity work.vga_sync
port map (
clk => clk,
hs => hs,
vs => vs,
r => r,
g => g,
b => b
);
CLOCK:
process
begin
wait for 20 ns; -- clock period 25 MHz = 40 ns;
clk <= not clk;
if now > 20 ms then -- one frame time plus a bit
wait;
end if;
end process;
end architecture;
It gave a vertical sync rate around 60 Hz:
Zooming in and measuring between two HS edges shows a horizontal rate of around 31.17 KHz.
You have horizontal and vertical blanking intervals and your R, G, and B does what your code says.
That sort of leaves the clock divider or something platform related.
Because a testbench for the clock is simple:
library ieee;
use ieee.std_logic_1164.all;
entity clock_tb is
end entity;
architecture foo of clock_tb is
signal clk: std_logic := '0';
signal clk25: std_logic;
begin
DUT:
entity work.clk_25mhz_divider
port map (
clk => clk,
clk_out => clk25
);
CLOCK:
process
begin
wait for 10 ns; -- half the period of 50 MHz
clk <= not clk;
if now > 130 ns then
wait;
end if;
end process;
end architecture;
It demonstrates Martin Zabel's answer:
That your divide by two actually divides by four. giving a period of 80 ns (12.5 MHz).
This demonstrates the usefulness of simulation and in simulation it can also be helpful to use signals instead of variables which have no history. Variables don't have a projected output waveform and he simulator has to attach extra code to display them in a waveform.
The simulation performance increase using variables instead of signals is traded for the ability to display them and there is no interesting distinction in synthesis.
From comments below question:
at that resolution i should use 25Mhz so i using the onboard clock
that's 50 Mhz and dividing it using the Clock divider module. –
Mostafa
Your clock divider divides the input frequency by 4 instead of 2. You toggle TEMP every two cycles of CLK which is CLKI of the top module. So a full cycle of CLK_OUT takes 4 cycles of the input clock.
To divide by two, you must toggle TEMP every clock cycle of the input clock:
architecture Behavioral of CLK_25MHz_Divider is
BEGIN
PROCESS(CLK)
VARIABLE TEMP : STD_LOGIC:='0';
BEGIN
IF RISING_EDGE(CLK)THEN
TEMP:=NOT TEMP;
END IF;
CLK_OUT<=TEMP;
END PROCESS;
end Behavioral;
Starting with TEMP = '0', it toggles to '1' at the first rising edge of CLK. At the second rising edge, TEMP toggles to '0', and at the third rising edge back to '1'. The duration between the first and third rising-edge of the 50 MHz input clock is 40 ns, which makes a frequency of 25 MHz for the output clock.
I have this circuit that I want to implement in vhdl. There is a clock input and which clock event changes the 1 pin output sequentially. 0001 -> 0010 -> 0100 -> 1000 ...
I wondering what is the correct approach to do that. I could do that with multiple ifs and elsifs and an integer counter signal. Sorry for the noob question, is there a name for this kind of circuit?
It appears from your description this intended to be a ring counter. Your gates seem superfluous:
library ieee;
use ieee.std_logic_1164.all;
entity ring_counter is
port (
clk: in std_logic;
q: out std_logic_vector (0 to 3)
);
end entity;
architecture your_representation of ring_counter is
signal qint: std_logic_vector (0 to 3) := "0000";
signal all_zero: std_logic;
begin
YOURS:
process(clk)
begin
if rising_edge(clk) then
qint(0) <= qint(3);
qint(1) <= all_zero or qint(0);
qint (2 to 3) <= qint(1 to 2);
end if;
end process;
all_zero <= '1' when qint = "0000" else
'0';
q <= (qint(0) or all_zero) & qint(1 to 3);
end architecture;
With a test bench:
library ieee;
use ieee.std_logic_1164.all;
entity ring_counter_tb is
end entity;
architecture foo of ring_counter_tb is
signal clk: std_logic := '0';
signal q: std_logic_vector(0 to 3);
begin
DUT:
entity work.ring_counter(your_representation)
port map (
clk => clk,
q => q
);
CLOCK:
process
begin
wait for 10 ns;
clk <= not clk;
if Now > 200 ns then
wait;
end if;
end process;
end architecture;
Gives:
(clickable)
While a classic ring counter:
architecture classic of ring_counter is
signal qint: std_logic_vector (0 to 3) := "1000";
begin
RING_CTR:
process(clk)
begin
if rising_edge(clk) then
qint <= qint(3) & qint(0 to 2);
end if;
end process;
q <= qint;
end architecture;
(and modified test bench):
entity work.ring_counter(classic)
gives:
(clickable)
And the starting phase is all in the initial condition.