2
\$\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;
asked Mar 3, 2021 at 9:18
\$\endgroup\$
0

3 Answers 3

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.

answered Mar 3, 2021 at 9:38
\$\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\$ Commented Mar 3, 2021 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\$ Commented Mar 3, 2021 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\$ Commented Mar 3, 2021 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\$ Commented Mar 3, 2021 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\$ Commented Mar 3, 2021 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;
answered Mar 3, 2021 at 9:43
\$\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\$ Commented Mar 3, 2021 at 9:45
  • \$\begingroup\$ Probably just personal preference, but it's easier to cast it into std_logic_vector \$\endgroup\$ Commented Mar 3, 2021 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.

answered Mar 8, 2021 at 9:02
\$\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\$ Commented Mar 20, 2021 at 14:15

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.