01 · PLL and clocks
Make any clock you need from the board's 50 MHz oscillator, and prove it: the design measures every clock it has and shows the result on the 7-segment display and on your PC.
The repo keeps the full project (.qar, plus the sources here) but no prebuilt bitstream: one build (a few minutes) makes the .sof on your computer.
What you will see
| Where | What |
|---|---|
| HEX5..HEX0 | Frequency of the selected clock in kHz, for example 25000 = 25 MHz, 100000 = 100 MHz |
| SW9 up | Cycles through the 8 clocks, one per second. SW9 down: SW2..SW0 choose one (0-4 = PLL outputs, 5 = MAX10_CLK2_50, 6 = ADC_CLK_10, 7 = MAX10_CLK1_50) |
| LEDR2..LEDR0 | Number of the clock on the display |
| LEDR8..LEDR4 | One blinker per PLL output. Each counts in its own clock, so all five blink at about 1 Hz |
| LEDR9 | PLL locked. Hold KEY0 to reset the PLL: LEDR9 goes off and the five blinkers stop |
| Your PC | python scripts/de10.py log prints one line per clock every second |
Block diagram
How the PLL is set
The PLL is the reusable lib/rtl/pll_de10.v: a direct instance of Altera's altpll
(the same thing the IP wizard writes) with each output set by three parameters:
pll_de10 #(
.C0_MUL(1), .C0_DIV(2), // 50 * 1/2 = 25 MHz
.C1_MUL(2), .C1_DIV(1), // 100 MHz
.C2_MUL(2), .C2_DIV(3), // 33.333 MHz
.C3_MUL(1), .C3_DIV(5), // 10 MHz
.C4_MUL(1), .C4_DIV(1), .C4_PHASE("5000") // 50 MHz, 5000 ps = 90 degrees late
) pll (.areset(~KEY[0]), .inclk0(MAX10_CLK1_50), .clk(pll_clk), .locked(locked));
Rule: fout = 50 MHz × MUL / DIV. Quartus finds a VCO frequency and counter values that make all five
outputs at once; if it can't, the build stops with an error naming the clock. What it actually built is in
output_files/pll_clocks.fit.rpt, section PLL Usage. For this example:
| Output | MUL/DIV | Frequency | Phase | Counter (from VCO 600 MHz) |
|---|---|---|---|---|
| c0 | 1/2 | 25.0 MHz | 0 | C0 = 24 |
| c1 | 2/1 | 100.0 MHz | 0 | C1 = 6 |
| c2 | 2/3 | 33.33 MHz | 0 | C2 = 18 |
| c3 | 1/5 | 10.0 MHz | 0 | C3 = 60 |
| c4 | 1/1 | 50.0 MHz | 90° (5000 ps) | C4 = 12 |
The VCO runs at 50 × M / N = 50 × 12 / 1 = 600 MHz, and each output divides it by its counter. Phase steps come in VCO steps of 208 ps, so a phase that is not a multiple of 208 ps is rounded (the report shows the value used).
Things to know
- Use
locked. PLL outputs are not stable untillockedis high. Hold your logic in reset until then; this example shows it on LEDR9. - Each clock is its own world. A signal that crosses from one clock to another must go through a synchronizer.
freq_countershows the safe pattern: a 1-bit gate crosses through two flip-flops, and the multi-bit count is read only while it is standing still. - Tell the timing analyzer. The
.sdcfile hascreate_clockfor the oscillator andderive_pll_clocks, so every PLL output is checked automatically. This example meets timing with no negative slack. - The DE10-Lite has three oscillators: MAX10_CLK1_50 and MAX10_CLK2_50 (50 MHz each) and ADC_CLK_10 (10 MHz). The MAX 10 has 4 PLLs; one is enough here.
Measured on the board
$ python scripts/de10.py log
c0 PLL 50*1/2 = 025000 kHz
c1 PLL 50*2/1 = 100000 kHz
c2 PLL 50*2/3 = 033333 kHz (33 333.3 kHz: shows 033333 or 033334)
c3 PLL 50*1/5 = 010000 kHz
c4 PLL 50 +90d = 050000 kHz
MAX10_CLK2_50 = 050000 kHz
ADC_CLK_10 = 010000 kHz
MAX10_CLK1_50 = 050000 kHz
Resources: 1,631 logic elements (3 %), 1 of 4 PLLs, 0 multipliers. Timing: no negative slack.
Run it
Build and load it
python scripts/de10.py build examples/01_pll_clocks
python scripts/de10.py program examples/01_pll_clocks
python scripts/de10.py log 3
Or open Quartus Prime Programmer, choose USB-Blaster, add output_files/pll_clocks.sof, tick Program/Configure, Start.
Build it yourself
python scripts/new_project.py examples/01_pll_clocks pll_clocks_top --lib pll print bcd freq sevenseg
python scripts/de10.py build examples/01_pll_clocks
python scripts/de10.py program examples/01_pll_clocks
Or restore pll_clocks.qar in Quartus (Project → Restore Archived Project); it holds the project and the
library files it uses, in their folders.
Test without the board
python scripts/sim.py examples/01_pll_clocks/sim/tb_freq_counter.sv # Icarus Verilog
python scripts/sim.py examples/01_pll_clocks/sim/tb_freq_counter.sv --modelsim # ModelSim
→ 25 MHz -> 25000 kHz, 10 MHz -> 10000 kHz PASS
This test caught a real bug while the example was being made: the first version lost one edge per gate and showed 24999.
Files
| File | What |
|---|---|
src/pll_clocks_top.sv | The example: PLL, counters, selector, display, report |
sim/tb_freq_counter.sv | Unit test of the frequency counter |
lib/rtl/pll_de10.v | Reusable PLL (5 outputs, parameters) |
lib/rtl/freq_counter.sv | Frequency counter with safe clock crossing |
lib/rtl/bin2bcd.sv, seven_segment.sv | Number to decimal digits to segments |
lib/rtl/line_printer.sv, jtag_uart.sv | Text lines to the PC over the USB cable |