The VHDL module "clock_divider" (see symbol)
creates a new clock with an integer or a non-integer multiple of the incoming clock period.
An earlier version of this module was used without any logical change in the UART module, which is also available from this website.
The module uses a simple counter to create the new clock.
So it is only possible to create periods which are integer multiples of the period of the incoming clock.
But when a non-integer multiple period has to be created, this period is approximated by mixing periods
with a length of n and n+1 periods of the system clock (n being the integer part of the requested period).
In this case only some of the rising edges of the new clock occur at the exact correct point in time,
while all the others have a (predictable) jitter. This jitter is kept as small as possible.
The module is configurable by generics in order
The module "clock_divider" was developed with HDL-SCHEM-Editor.
Port name | Direction | Description |
---|---|---|
res_i | input | asynchronous reset input, 1-active |
clk_i | input | clock input |
enable_clock_divider_i | input | If 0, then clock_divided_o is equal 0 permanentely. After changing from 0 to 1, the next rising edge of clk_i creates the first rising edge at clock_divided_o. |
clock_period_int_part_i(g_period_width-1:0) | input | Period of the clock created at clock_divided_o as unsigned integer number of system clocks at clk_i. The input must be stable during the clock creation. |
clock_period_fract_part_nominator_i (g_fraction_width-1:0) |
input | Additional unsigned nominator fraction part of a system clock which has to be added to clock_period_int_part_i. If 0, then clock_period_fract_part_denominator_i is ignored. The input must be stable during the clock creation. |
clock_period_fract_part_denominator_i (g_fraction_width-1:0) |
input | Additional unsigned denominator fraction part of a system clock which has to be added to clock_period_int_part_i. This value must be bigger than clock_period_fract_part_nominator_i. The input must be stable during the clock creation. |
clk_divided_o | output | Divided clock with the period (counted in system clocks): (clock_period_int_part_i + clock_period_fract_part_nominator_i/clock_period_fract_part_denominator_i). If generic g_create_clock_enable=0, then the falling edge occurs always clock_period_int_part_i/2 system clocks after the rising edge at clkc_divided_o. If generic g_create_clock_enable=1, then the falling edge occurs always 1 system clock after the rising edge at clk_divided_o. |
Generic name | Minimum Value | Maximum Value | Description |
---|---|---|---|
g_period_width | 2 | none | Number of bits of clock_period_int_part_i To keep the hardware simple this value should be as small as possible. |
g_fraction_width | 2 | none | Number of bits of clock_period_fract_part_nominator_i and clock_period_fract_part_denominator_i To keep the hardware simple this value should be as small as possible. |
g_create_clock_enable | false | true | If false, then a clock signal is created at clk_divided_o. If true, then a clock enable signal is created at clk_divided_o. |
The clock_divider module shall create a clock whose period is n-times longer than that of the incoming clock.
The factor n is defined as (with '/' being a division with a real number result):
n = clock_period_int_part_i + clock_period_fract_part_nominator_i/clock_period_fract_part_denominator_i
However, since the clock_divider module will create the new clock using only a simple counter,
the desired period must be approximated by creating a new clock whose period alternates
between clock_period_int_part_i and clock_period_int_part_i+1 times the incoming period.
In this way after clock_period_fract_part_denominator_i periods of the new clock, the new clock will always have a rising clock edge
that occurs at an exact correct point in time, as multiplying n with clock_period_fract_part_denominator_i creates an integer number:
clock_period_fract_part_denominator_i*n = clock_period_fract_part_denominator_i*clock_period_int_part_i + clock_period_fract_part_nominator_i
The timing of all other rising clock edges will not be exact, but will behave as if the new clock had a jitter.
This jitter is kept as small as possible.
To make this description better readable the following abbreviations are used:
S = period in unit time of the incoming system clock clk_i
C = period in unit time of the outgoing clock clk_divided_o
I = clock_period_int_part_i
N = clock_period_fract_part_nominator_i
D = clock_period_fract_part_denominator_i
The new clock shall have the period:
C = (I + N/D) * S
But the used counter can only create too short or too long periods:
C_short = I * S
C_long = (I + 1) * S
When using C_short, the error is:
I*S - (I + N/D)*S = -N/D * S
When using C_long, the error is:
(I + 1)*S - (I + N/D)*S = (1-N/D) * S
When divided by S, the resulting error has the unit "number of system clocks".
When multiplying with D the resulting error E is too big by factor D, but is an integer number:
E_short = -N , always < 0
E_long = D-N , always > 0
This error E is used by the algorithm and has the value 0 at the first edge of the new clock.
The algorithm always starts with a too short period, so at the second edge, E will have the value -N.
When the algorithm detects E < 0, it uses periods with C_long (always adding D-N to E).
When the algorithm detects E >=0, it uses periods with C_short (always adding -N to E).
After D periods of the new clock, E will always have the value 0.
E gets the smallest value when E=0 and -N is added, which gives E=-N (if E>0 the result cannot get any smaller).
E gets the biggest value when E=-1 and D-N is added, which gives E=D-N-1 (if E<-1 the result cannot get any bigger).
Therefore E is limited between:
-N <= E <= D-N-1
When E is multiplied by S and divided by D, the result is the jitter (in unit time) of the clock
edge of the divided clock (so E is called "jitter" in the implementation).
The jitter is limited between:
-S*N/D <= jitter (unit time) <= S*(D-N-1)/D
The jitter in unit percentage compared to the exact period of the new clock is:
-S*N/(D*C) <= jitter (unit percentage) <= S*(D-N-1)/(D*C)
-S*N/(D*(I + N/D) * S) <= jitter (unit percentage) <= S*(D-N-1)/(D*(I + N/D) * S)
-N/(D*(I + N/D)) <= jitter (unit percentage) <= (D-N-1)/(D*(I + N/D))
-N/(D*I+N) <= jitter (unit percentage) <= (D-N-1)/(D*I+N)
Example: Create 115.2 KHz UART clock from 100 MHz system clock:
clock_period_int_part_i = 868
clock_period_fract_part_nominator_i = 1
clock_period_fract_part_denominator_i = 18
jitter = -0.0064% .. +0.10%
Source code for HDL-SCHEM-Editor for module "clock_divider" and its testbench (Number of downloads =
148 ).
With these files the schematic of the clock_divider module can be loaded into HDL-SCHEM-Editor and can be easily read and modified:
All module VHDL-files of the clock_divider module (Number of downloads =
122 ).
These files were generated by HDL-SCHEM-Editor:
All testbench VHDL-files of the clock_divider module (Number of downloads =
159 ).
These files were generated by HDL-SCHEM-Editor:
You should extract all archives into a folder named "clock_divider".
Then you should load the toplevel (probably the testbench) into HDL-SCHEM-Editor.
When you navigate through the design hierarchy by a double click at each symbol,
HDL-SCHEM-Editor will find the submodules on your disk and ask if it can replace
the original path to the submodule by the new one at your disk.
After storing the changed modules the relocation of the source files is ready
(instead you could replace "M:/gesicherte Daten/Programmieren/VHDL/clock_divider" in all
"hdl_editor_designs/*.hse" source files by your path to this directory with your editor).
Now you can navigate through the design by HDL-SCHEM-Editor and generate HDL by HDL-SCHEM-Editor.
Of course there is only need for generating HDL, if you change something at the module,
because you can find the HDL in VHDL_designs.zip and VHDL_testbenches.zip.
If you want to simulate or modify the modules by HDL-SCHEM-Editor you also must adapt the information in the Control-tab of the toplevel you want to work on.
There you must define a "Compile through hierarchy command", an "Edit command" and a "Working directory".
Version 1.0 (02.06.2025):
If you detect any bugs or have any questions,
please send a mail to "matthias.schweikart@gmx.de".