1
votes

So I have a simple 2 bit counter that moves from one state to the next on a button press. However, the only clock I have access to runs at 125MHz, which is too fast for the button press, so I need to divide the clock down to a more reasonable speed. I've seen a few examples of clock dividers on this site, however what I can't figure out is:

  1. How do I add the clock divider to existing VHDL code, do I just add the divided clock as a new output or input port? and how do I set it up so the state change will only occur following the divided clock?
  2. In the constraint file, how do I include the divided clock? I think I use generated clock as part of the statement, but does it have to be assigned to its own seperate pin? Right now I have the main clock assigned as:

    set_property PACKAGE_PIN L16 [get_ports clk]

    set_property IOSTANDARD LVCMOS33 [get_ports clk]

    create_clock -period 10.000 -name clk -waveform {0.000 5.000} [get_ports clk]

Here is the VHDL code:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity des_src is
    Port ( clk : in STD_LOGIC;
           BTN_0 : in STD_LOGIC;
           LED_1 : out STD_LOGIC;
           LED_0 : out STD_LOGIC);
end des_src;

architecture Behavioral of des_src is
    TYPE        statetype IS (Start, One, Two, Three);
    SIGNAL      currentstate, nextstate : statetype;

begin
    fsm1:   PROCESS (currentstate, BTN_0)
    BEGIN
        CASE currentstate IS
            WHEN Start =>
                LED_1 <= '0';
                LED_0 <= '0';
                CASE BTN_0 IS
                    WHEN '1' =>
                        nextstate <= One;
                    WHEN OTHERS =>
                        nextstate <= Start;
                END CASE;
            WHEN One =>
                LED_1 <= '0';
                LED_0 <= '1';
                CASE BTN_0 IS
                    WHEN '1' =>
                        nextstate <= Two;
                    WHEN OTHERS =>
                        nextstate <= One;
                END CASE;
            WHEN Two =>
                LED_1 <= '1';
                LED_0 <= '0';
                CASE BTN_0 IS
                    WHEN '1' =>
                        nextstate <= Three;
                    WHEN OTHERS =>
                        nextstate <= Two;
                END CASE;
            WHEN Three =>
                LED_1 <= '1';
                LED_0 <= '1';
                CASE BTN_0 IS
                    WHEN '1' =>
                        nextstate <= Start;
                    WHEN OTHERS =>
                        nextstate <= Three;
                END CASE;
        END CASE;
    END PROCESS;

    fsm2:   PROCESS (clk)
    BEGIN
        IF (clk'EVENT) AND (clk = '1') THEN
            currentstate <= nextstate;
        END IF;
    END PROCESS;

end Behavioral;

I'm programming for the ZYBO using Vivado 2015.2 Any and All help is appreciated, Thanks!

1

1 Answers

1
votes

First of all (I'll answer your question on clock dividing later), you do not need a clock divider to design your count-on-press-button system. You need an edge detector, which is completely different and runs at 125 MHz. It is a synchronous device that detects a rising (or falling) edge of an input signal and asserts its output during one clock period only when the edge has been detected. And you can use this output to trigger the increment of your 125 MHz running counter.

But this is not the whole story: as your press-button is not synchronous of your master clock you cannot use it as is. If you did, there would be a risk that its value changes just at the rising edge of the clock (or very close from it) and that the first register on the path samples neither a one nor a zero. The register could then enter what we call a metastability, which is highly undesirable, especially if it propagates.

Before using your press-button input you must synchronize it with a 2 or 3 stages shift register. The reason why it works and why 2 or 3 stages is out of scope this answer and can be found in many text books. There is also another issue with press-buttons: they sometimes bounce between their two states before stabilizing. Your Zybo is probably equipped with resistors that somehow help but in case your counter does not behave as expected and does not always increment by just one when you press the button, you will need a "debouncer" to filter out the extra edges. But this is a too long story for this answer.

Anyway, the following code does the two (synchronization and edge detection):

signal pipe: std_ulogic_vector(0 to 2);
signal tick: std_ulogic;
...
process(clk)
begin
  if rising_edge(clk) = '1' then
    pipe <= press_button & pipe(0 to 1);
  end if;
end process;

tick <= pipe(1) and (not pipe(2));

That's it, tick will be asserted during one clock period every time you press the button, whatever when you press it compared to the 125 MHz clock edges and whatever how long you press it... well, if you press it for at least one clock period, but unless you are Superman himself, it should always be the case.

Now for your counting system, I think it is too low level (VHDL is a High Level language). So, if you want to count, use integers or some other type on which arithmetic is defined (like ieee.numeric_std.unsigned, for instance):

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
...
signal counter: unsigned(1 downto 0);
...
process(clk)
begin
  if rising_edge(clk) then
    counter <= counter + tick;
  end if;
end process;
LED_0 <= counter(0);
LED_1 <= counter(1);

Note: unless you are sure that counter will be automatically reset at power-up, you should also use a kind of reset to initialize it:

process(clk)
begin
  if rising_edge(clk) then
    if reset = '1' then
      counter <= "00";
    else
      counter <= counter + tick;
    end if;
  end if;
end process;

Finally, if you really need a clock divider, you can use a counter (just like the one above but always counting) and use its most significant bit as a clock. But you will have to tell Vivado such that it inserts a clock buffer at the right place. And here, the Vivado documentation is your friend. Alternately you can instantiate a clock manager that can generate clocks from a primary clock, at many different frequencies, and not only at integer dividers of the frequency of the primary clock. Here again, the Xilinx documentation is your friend.