Files
gps/hardware/external/gps/rtl/pps/README.md
T

13 KiB

GPS PPS peripheral

pps_wrapper is an APB-controlled, 125 MHz disciplined GPS timebase. Its raw 64-bit counter is immutable after reset. Tracking-channel measurement events capture that counter into a per-channel bank; software reads the channel used as the WLS timing datum and commits this atomic mailbox:

{GPS_TIME_AT_CAPTURE, EPOCH_COUNTER, EPOCH_SEQUENCE}

The core extrapolates the GPS reference to the commit instant from the counter age. Before coarse alignment it uses nominal rate; afterwards it uses the current disciplined rate estimate, preventing oscillator_ppm * compute_delay from leaking into phase. CPU/WLS/APB latency is therefore not interpreted as PPS phase. A coarse commit aligns the separate disciplined accumulator. Later commits update a signed fixed-point PI rate word; holdover freezes the last rate word. The raw counter is never slewed or reset by a navigation solution.

GPS_FRACTION_Q32 is a Q0.32 fraction of one GPS second. KP and KI are signed 48-bit Q2.46 discrete coefficients. Q2.46 is required because the 125 MHz clock makes the low-bandwidth integral coefficient smaller than one Q2.30 LSB. Phase, integrator, and rate diagnostics are signed Q32 counter-tick values. MAX_SLEW_Q32 is the absolute rate limit in Q32 ticks per 125 MHz clock; the reset value is approximately 20 ppm.

The PI controller is the hardware loop filter: Kp supplies proportional phase correction and Ki is the retained frequency/integral state. There is no additional phase IIR and no derivative term. An extra phase IIR was modelled and rejected because it duplicated the small navigation pre-filter and added acquisition/profile-transition delay. Software may change Kp, Ki, maximum slew and lock policy while running; the core captures the whole mailbox coherently at the next commit and preserves the integrator across a normal profile transition.

Each rising PPS edge is held high for 100 ms (12,500,000 tracking-clock cycles at 125 MHz), making the physical output easy to observe without changing the disciplined edge timing. The width is derived from CLK_FREQ_HZ and the shared package constant, so reduced-clock unit tests retain the same 10% duty cycle.

All interface, state, coefficient, intermediate-product and default widths are defined once in pps_pkg.sv. PPS_PRODUCT_WIDTH is derived as PPS_STATE_WIDTH + PPS_COEFF_WIDTH (64 + 48 = 112). It is the full-precision combinational multiply result, not a 112-bit APB field or retained time state.

The hardware does not add POSTPONE_TOW. Baremetal must commit an already epoch-aligned GPS reference, so the verified 160 ms subframe readout correction is owned exactly once by the navigation software.

Tracking C/N0 and Doppler are not inputs to the PPS RTL. The tracking wrapper publishes those measurements through its separate 20 ms APB mailbox. Firmware uses C/N0, geometry, WLS clock sigma, integrity and Doppler velocity residual to select acquire, precision, robust or holdover, then writes the corresponding PI coefficients, slew limit and lock thresholds through this PPS mailbox. Doppler also predicts the WLS clock-bias datum between commits. This keeps quality policy and the small navigation pre-filter in software while the deterministic PI loop, fixed-point rate state and physical edge generation remain in hardware.

In a conventional absolute-pseudorange model, receiver clock bias and receiver clock drift are the level and slope of the same physical clock: b_r = c*delta_t_r and bdot_r = c*d(delta_t_r)/dt. The position-WLS bias in this project is different: subtracting the common capture time and adding STARTOFFSET makes it a reference-satellite-relative datum, approximately c*STARTOFFSET - reference_range, rather than a pure receiver oscillator offset.

The Doppler WLS still solves a fourth, common receiver clock/frequency-drift state. The PPS pre-filter does not use that state directly as the derivative of the position-WLS datum. Instead, it predicts that datum with:

position_datum_drift =
    -reference_geometric_range_rate
    + c * reference_satellite_clock_drift

The geometric term uses the Doppler-WLS receiver velocity plus the selected reference satellite position and velocity. The PPS RTL sees only the resulting timestamped reference and loop configuration.

The APB register offsets live in pps_pkg.sv. Multiword input fields are shadow registers and must be followed by one COMMIT write. A new mailbox must not be written while STATUS.COMMIT_BUSY is set.

The capture_i vector is synchronous to PCLK. Each pulse stores the Gray-synchronized 125 MHz counter and increments that channel's capture sequence. The two-stage crossing adds a small fixed timestamp pipeline offset; the SoC-level connection must use the exact PCLK event that updates ABS_SAMPLE/REM_CODE_PHASE. CAPTURE_COUNTER initially refers to the integer RF-sample event. Before writing EPOCH_COUNTER, navigation software subtracts

round((REM_CODE_PHASE / 1.023e6) * (125e6 / RF_HZ))

counter ticks, so the counter and corrected WLS observation refer to the same fractional code boundary. This removes an otherwise variable 0--61 ns term. The remaining PCLK/Gray pipeline delay is fixed and must be measured once on hardware.

APB register map

Offset Register Access Meaning
0x00 CONTROL RW bit 0 enable output, bit 1 track enable, bit 2 holdover, bit 3 clear sticky status (pulse)
0x04 STATUS RO aligned/valid/busy/stale/saturation/overrun state; bit 11 is PPS loop lock
0x08/0x0c RAW_COUNTER RO immutable 125 MHz counter; reading low latches high
0x10 CAPTURE_SELECT RW tracking-channel capture bank index
0x14/0x18 CAPTURE_COUNTER RO selected channel's captured counter
0x1c CAPTURE_SEQUENCE RO selected channel's capture count
0x20/0x24 EPOCH_COUNTER shadow counter paired with the WLS reference
0x28/0x2c GPS_SECONDS shadow continuous GPS seconds at the captured epoch
0x30 GPS_FRACTION_Q32 shadow Q0.32 fraction of that GPS second
0x34 EPOCH_SEQUENCE shadow modular freshness tag
0x38/0x80 KP_Q46_LO/HI RW shadow signed 48-bit Q2.46 proportional coefficient
0x3c/0x84 KI_Q46_LO/HI RW shadow signed 48-bit Q2.46 integral coefficient
0x40 MAX_SLEW_Q32 RW shadow absolute Q32 ticks/clock rate clamp
0x44 COMMIT WO bit 0 commit, bit 1 coarse-align commit
0x48..0x74 diagnostics RO sequence, GPS time, phase, integrator, rate and pulse count
0x78 DIAG_SNAPSHOT WO bit 0 atomically snapshots all multiword diagnostics
0x7c VERSION RO RTL interface version (0x00020200)
0x88 LOOP_CONFIG RW shadow bits 15:8 acquire ticks, 23:16 release ticks, 31:24 consecutive acquire updates; bits 7:0 reserved

Coarse alignment does not assert lock or drive the pin. The reset policy requires five consecutive raw PI residuals within eight tracking-clock ticks (64 ns at 125 MHz); a residual above 32 ticks (256 ns) releases lock. Runtime profiles may widen the release threshold or require more good updates without filtering the phase seen by the controller. Holdover retains the internal frequency/lock state but deasserts STATUS.VALID; a newly committed bad reference drops lock immediately. STATUS.LOCKED, STATUS.VALID, and the physical PPS output therefore distinguish a held/aligned timebase from a currently qualified one.

Firmware uses a two-second no-reference watchdog to enter holdover but keeps the Doppler-predicted bias and PI state for bounded same-datum gaps. On recovery it fills all mailbox shadow words, releases holdover immediately before the normal commit toggle, and therefore applies the first coherent post-gap reference (v2.2 rejects normal PI commits that arrive while holdover is still asserted). The physical PPS already runs from the held rate word and does not phase-step; STATUS.VALID may precede the PI apply only by the bounded synchronizer/commit-pipeline latency. A datum change or an observation gap beyond the prediction horizon uses a coarse commit and remains invalid until lock is reacquired.

Diagnostic words do not change until another DIAG_SNAPSHOT request completes; software polls STATUS.DIAG_BUSY before reading the group. The disciplined timebase continues in holdover and while the physical output is disabled; CONTROL.ENABLE gates pps_out and validity, not timekeeping.

This also applies to the single-word ACTIVE_SEQUENCE and PULSE_COUNT registers: they belong to the same frozen diagnostic group. A live debugger must halt the CPU briefly, request a new snapshot, wait for status bit 10 to clear, and only then read 0x48..0x74. For example, with OpenOCD's GDB monitor:

monitor halt
monitor mww 0x1002a078 1
monitor sleep 10
x/wx 0x1002a004
x/wx 0x1002a048
x/wx 0x1002a05c
x/wx 0x1002a060
x/wx 0x1002a074
monitor resume

Reading ACTIVE_SEQUENCE or PULSE_COUNT without this request can return the previous snapshot, including reset zero, even while the core is accepting commits and generating pulses.

Run lint plus all five Questa tests from this directory:

make -C tb check

pps_wrapper_tb uses the same ordered MMIO writes as gps_system/src/gps_pps.c and injects CPU/APB latency; it separately verifies that a held normal commit is not applied and that the firmware's shadow-first release sequence applies the first recovery update. pps_core_tb independently stresses signed PI math, slew/anti-windup saturation, stale/future epochs, phase saturation, sequence wrap, holdover and output disable. pps_convergence_tb supplies an independent GPS truth clock, a +15 ppm oscillator error followed by a +8 ppm temperature-like step, deterministic measurement noise, varying 3--27 cycle software latency, and 5 Hz navigation commits. It checks phase and rate convergence numerically. pps_adaptive_tb changes Kp, Ki, maximum slew and lock policy while the loop is live, checks integrator preservation, validates raw-residual lock acquisition/release, and verifies that holdover suppresses validity. CLK_FREQ_HZ is reduced only to shorten simulation; tick/Q-format behavior is unchanged.

The convergence waveform also exports real-valued views: phase/mapping/PPS errors in counter ticks, rate and target rate in ppm, plus fixed-scale normalized errors. wave.do opens these as analog plots automatically. Its test clock period is 1 second / CLK_FREQ_HZ, so adjacent PPS pulses are one second apart on Questa's displayed time axis even though the event count is reduced for simulation speed.

For an interactive waveform in a second terminal:

cd hardware/external/gps/rtl/pps/tb
./run.sh          # closed-loop convergence (default)
./run.sh converge # same as the default
./run.sh precision # fixed-point/quantization sweep
./run.sh core     # arithmetic/limit corner cases
./run.sh wrapper  # APB and CDC mailbox view

SoC integration and physical accuracy

The peripheral is mapped at 0x1002a000; it has no PLIC interrupt. Tracking channels drive capture_i only from their nav-solve event, and the generated Rocket/VCU118 and Genesys2 tops expose gps_pps as an output. VCU118 routes it to the otherwise-unused PMOD1/J53.8 signal (R29, FPGA-side LVCMOS12 through the board's 3.3 V level shifter). Genesys2 routes it to the otherwise-unused PMOD JC10 signal (AK28, LVCMOS33). Both use 8 mA drive and FAST slew.

At 125 MHz a PPS edge can occur only on an 8 ns grid. The fractional accumulator provides much finer long-term frequency resolution, but it cannot place one edge between clock edges. Absolute accuracy also contains WLS, atmosphere/multipath, clock-distribution and the fixed PCLK/Gray-capture offset; the latter must be measured and calibrated on the board. Thus this design is nanosecond-order hardware, not a claim of 1 ns absolute accuracy.

Functional precision boundary

pps_precision_tb.sv runs the same 5 Hz discrete PI scaling with a +15 ppm to +8 ppm oscillator step and reports the steady fixed-point limit cycle. It separates range width from fractional precision and writes pps_precision.csv. For this nominal loop:

  • coefficient Q46 and Q40 both give about 0.57 ns RMS; Q39 gives 1.74 ns, Q38 gives 4.06 ns, Q36 gives 13.61 ns, and Q32 loses Ki entirely;
  • keeping Q46 while reducing the coefficient word to signed 19 bits is bit-identical for the nominal gains; 18 bits saturates Kp and raises the floor to about 21 ns. A 24-bit word leaves about 37x Kp tuning headroom;
  • GPS fraction Q30 is below 1 ns resolution, Q28 produces about 1.74 ns RMS, and Q26 about 6.90 ns RMS in this deterministic case;
  • phase storage needs only four fractional tick bits for a sub-ns loop floor here, but rate word Q32 remains important: Q31 gives about 14 ns and Q30 about 42 ns of steady phase offset due to accumulated rate quantization;
  • the independent 125 MHz output grid contributes 8/sqrt(12) = 2.31 ns RMS for uniformly distributed edge quantization, even if the control arithmetic is finer.

These numbers are an arithmetic quantization floor, not total PPS accuracy; WLS, RF clock, atmosphere, multipath and board I/O delay remain separate.