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;
3 Answers 3
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.
-
\$\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– David7772021年03月03日 10:58:46 +00:00Commented 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\$DonFusili– DonFusili2021年03月03日 11:03:15 +00:00Commented 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\$Stack Exchange Broke The Law– Stack Exchange Broke The Law2021年03月03日 11:42:06 +00:00Commented 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\$DonFusili– DonFusili2021年03月03日 11:49:33 +00:00Commented 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 anelsif
, just like in any programming language. In the second process, I resetr_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– DonFusili2021年03月03日 14:05:39 +00:00Commented Mar 3, 2021 at 14:05
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;
-
\$\begingroup\$ Why would you prefer
unsigned
overinteger
s? They simulate slower. I second the decrementing, though, it's a better pattern for counting in general. \$\endgroup\$DonFusili– DonFusili2021年03月03日 09:45:34 +00:00Commented Mar 3, 2021 at 9:45 -
\$\begingroup\$ Probably just personal preference, but it's easier to cast it into
std_logic_vector
\$\endgroup\$po.pe– po.pe2021年03月03日 09:47:22 +00:00Commented Mar 3, 2021 at 9:47
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.
-
\$\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– David7772021年03月20日 14:15:25 +00:00Commented Mar 20, 2021 at 14:15