
This is an All-Digital Phase-Locked Loop (ADPLL) that locks an internal ring oscillator to an external 32.768 kHz crystal reference, synthesizing a 16.777216 MHz output clock (x512 frequency multiplication).
+---------------------------+
ref_clk ----+--------->| TDC (Time-to-Digital |
32.768 kHz | | Converter) |
| | Counts DCO edges per |
| | ref_clk period |
| +------------+----------------+
| | phase_error
| v
| +---------------------------+
| | PI Loop Filter |
| | Kp, Ki set via pins |
| +------------+----------------+
| | dco_control[11:0]
| v
| +---------------------------+
| | DCO (Ring Oscillator) |
| | 63-stage with tunable |
| | length + switchable loads |
| +------------+----------------+
| | dco_clk
| +-------+-------> dco_clk_out
| |
| +---------------------------+
| | Divider (/512) |
| +------------+----------------+
| |
+--- should match ----> +--------> div_clk_out
when locked
TDC: Counts rising edges of dco_clk between consecutive ref_clk rising edges. Computes phase_error = 512 - measured_count. Positive error means DCO is too slow.
PI Loop Filter: Proportional + integral controller using arithmetic right-shifts (no multipliers). Gains are programmable via kp_sel and ki_sel pins. Includes anti-windup clamping.
DCO: On silicon, a digitally-controlled ring oscillator with coarse tuning (selectable ring length, 3-63 stages) and fine tuning (switchable capacitive loads). In simulation, a behavioral delay model.
Lock Detector: Asserts locked when |phase_error| < 4 for 16 consecutive reference cycles. De-asserts after 4 consecutive out-of-range cycles (hysteresis).
Divider: Divide-by-512 counter on the DCO output. When locked, div_clk_out should match ref_clk.
| Parameter | Value | Description |
|---|---|---|
| DCO_WIDTH | 12 | DCO control word width |
| COUNT_WIDTH | 16 | TDC counter width |
| TARGET_COUNT | 512 | Expected DCO cycles per ref period |
| LOCK_THRESHOLD | 4 | Max |
| LOCK_COUNT | 16 | Consecutive good cycles to lock |
ui_in[0] (ref_clk)ui_in[3:1] (kp_sel) and ui_in[6:4] (ki_sel):
ui_in[7] (loop_enable) to start the PLLuo_out[0]: DCO clock output (~16.777 MHz when locked)uo_out[1]: DCO / 512 — should match ref_clk frequency when lockeduo_out[2]: Lock indicator (high = locked)uo_out[3]: Phase error sign (0 = DCO slow, 1 = DCO fast)uo_out[7:4]: Lower 4 bits of |phase_error| (should be near 0 when locked)uio_out[7:0]: Upper 8 bits of DCO control word (for debug)locked stays highdiv_clk_out frequency matches ref_clk within the DCO's quantization limit# cocotb tests (recommended)
cd test && make
# Local iverilog simulation (behavioral model)
make sim
# View waveforms
gtkwave adpll.vcd
ui_in[0]uo_out[0] to verify output frequency| # | Input | Output | Bidirectional |
|---|---|---|---|
| 0 | ref_clk (32.768 kHz crystal input) | dco_clk_out (DCO output clock) | dco_control[4] |
| 1 | kp_sel[0] - proportional gain select | div_clk_out (DCO / 512, matches ref_clk when locked) | dco_control[5] |
| 2 | kp_sel[1] | locked (lock indicator) | dco_control[6] |
| 3 | kp_sel[2] | phase_error sign | dco_control[7] |
| 4 | ki_sel[0] - integral gain select | |phase_error|[0] | dco_control[8] |
| 5 | ki_sel[1] | |phase_error|[1] | dco_control[9] |
| 6 | ki_sel[2] | |phase_error|[2] | dco_control[10] |
| 7 | loop_enable | |phase_error|[3] | dco_control[11] |