1
\$\begingroup\$

I have a question about the process that I wrote to divider a 1MHz clock down to a 10kHz clock.

I don't know why my code always splits up like it does below, if someone can edit it to look better and let me know what I am doing wrong.

Now my question is how are the if and elsif statements inside the process performed? On the rising edge of the clock, the Count signal will be incremented by 1. But will the elsif statement ever be executed as the if statement will have to be true?

Include libraries / packages

Entity Clock_Divider is
 Port (Clock_in : in std_logic;
       Clock_out : out std_logic
      );
end Clock_Divider;

Architecture Behavioral of Clock_Divider is

signal Count : integer := 1;
    
signal Temp : std_logic := '0';

begin

    Process (Clock_in)

    begin

    if (rising_edge(Clock_in)) then
           Count  <= Count + 1;

        elsif (Count = 100000) then
              Temp <= not Temp;

        end if;

        Clock_out <= Temp;

    end Process;

end Behavioral;
\$\endgroup\$
0
2
\$\begingroup\$

This will "work" in simulation, for a bad definition of "to work".

Assuming a normal clock, this are the process invocations:

  • Clock rises, if(rising_edge(clock_in)) is taken, you increment Count
  • Clock falls, the elsif is checked, if you reached the limit on the previous rising edge, you will toggle Temp and then update the out clock.

As you can see, you rely on the sensitivity list for logic, that's never a good idea. Both because it's more obscure code and because it likely won't do what you want post-synthesis, where most toolchains just ignore sensitivity lists alltogether (as they should).

A cleaner solution for a clock divider would be:

p_clock_div : process(clk_in)
  variable r_count : integer range 0 to c_count_max := c_count_max;
  variable r_clk_out_i : std_logic := '0';
begin
  if rising_edge(clk_in) then
    r_count := r_count - 1;
    if r_count = 0 then
      r_clk_out_i := not r_clk_out_i;
      r_count := c_count_max;
    end if;
  end if;
  clk_out <= r_clk_out_i;
end process;

Or, if you want that falling edge update, then show so in your code:

p_clock_div : process(clk_in)
  variable r_count : integer range 0 to c_count_max := c_count_max;
  variable r_clk_out_i : std_logic := '0';
begin
  if rising_edge(clk_in) then
    r_count := r_count - 1;
  elsif falling_edge(clk_in) then
    if r_count = 0 then
      r_clk_out_i := not r_clk_out_i;
      r_count := c_count_max;
    end if;
  end if;
  clk_out <= r_clk_out_i;
end process;

Note that some (mainly ASIC targetting) synthesizers don't like the clock_out <= r_clock_out_i line to be outside the if tree.

Also note that clock_out has an undefined startup state and this whole block would be cleaner if you had a clk_in-synchronized reset input signal.

\$\endgroup\$
10
  • \$\begingroup\$ Thanks Don, I see how this is bad code. I don't intend to use the falling edge of the clock so I will change to have all the signal assignment inside the elsif ((rising_edge)). What is your opinion about having the clock_out assignment inside the process instead of outside of the process? \$\endgroup\$ – David777 Mar 3 at 10:58
  • 1
    \$\begingroup\$ @David777 I think you should do everything inside a single process per clock domain in a module. If you do that and use variables instead of signals wherever possible, your code is easier to read and automatically avoids delta cycle issues. There are... philosophical arguments about the subject, however. \$\endgroup\$ – DonFusili Mar 3 at 11:03
  • 2
    \$\begingroup\$ @DonFusili It always astonishes me that we are still using this language in an unintended way and using hacky workarounds to make it fit. Why not a language where you can only have a single process per clock domain in a module? \$\endgroup\$ – user253751 Mar 3 at 11:42
  • 1
    \$\begingroup\$ @user253751 Tbf, the "description" in VHDL has been meaningless for a while now and most of the issues the language has as an implementation language disappear if you use it properly. Properly sadly being "the other way than most respected people in the field have taught themselves over the last few decades". But yeah, a clean language would probably be better... could be worse, though, could be verilog. \$\endgroup\$ – DonFusili Mar 3 at 11:49
  • 1
    \$\begingroup\$ @David777 I'm not sure which of the two written processes you are talking about, but every if statement will be checked, unless they use an elsif, just like in any programming language. In the second process, I reset r_count on the falling edge of the clock. A signal cannot have a rising and a falling edge in the same delta cycle. \$\endgroup\$ – DonFusili Mar 3 at 14:05
2
\$\begingroup\$

This won't work as it creates a latch on your Temp signal. You should perform all the signal manipulation within your if rising_edge(clk) then and last but not least you have to reset your Count. In addition it might make sense to have a reset signal. Use a counter signal with a defined width, I'd even prefer unsigned over integer. Then, to save some resources, it's easier to load the counter with the starting value and decrement instead of increment, then check for the MSB = '1' what will be the case if the counter overflows.

ARCHITECTURE behavioural OF clockdivider IS
  SIGNAL counter : std_logic_vector(log2_ceil(div_value)+1 DOWNTO 0);
  SIGNAL clk_int : std_logic;
BEGIN

  clk_out <= clk_int;

  clk_div : PROCESS(clk_i,rst_i)
  BEGIN
      IF div_value = 0 THEN
      clk_int <= clk_i;
  
    -- asynchronous reset
    ELSIF rst_i = '1' THEN
      counter <= std_logic_vector(to_unsigned(div_value-2, counter'LENGTH));
      clk_int <= '0';

    ELSIF rising_edge(clk_i) THEN
            IF enable = '1' THEN
                IF counter(counter'LEFT) = '1' THEN
                    clk_int <= NOT(clk_int);
                    counter <= std_logic_vector(to_unsigned(div_value-2, counter'LENGTH));
                ELSE
                    counter <= std_logic_vector(signed(counter) - 1);
                END IF;
            ELSE
                clk_int <= '0';
            END IF;

    END IF;
  END PROCESS clk_div;
  
END ARCHITECTURE behavioural;
\$\endgroup\$
2
  • \$\begingroup\$ Why would you prefer unsigned over integers? They simulate slower. I second the decrementing, though, it's a better pattern for counting in general. \$\endgroup\$ – DonFusili Mar 3 at 9:45
  • \$\begingroup\$ Probably just personal preference, but it's easier to cast it into std_logic_vector \$\endgroup\$ – po.pe Mar 3 at 9:47
2
\$\begingroup\$

For real implementation, better way is not to tweak the clock signal and to reroute it somewhere else, but to provide a "clock enable" signal that is enabled every 100'000 cycle.

Port the original clock (100 MHz) in the module that requires the 10 KHz clock. Modify the module so that its logic is enabled only if the clock_ena signal from the divider is high at the rising edge of original 100 MHz clock.

\$\endgroup\$
1
  • \$\begingroup\$ I am curious as to why this is better for 'real implementation'? So you mean to still keep the clock divider module and inside it produce a clock enable signal for the other module that needs the slow clock? \$\endgroup\$ – David777 Mar 20 at 14:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.