feat(gps): add timer-based assisted reacquisition

Remove the fixed pseudorange start offset, preserve per-PRN navigation assistance across RF loss and power cycles, and seed coherent navigation measurement phase on reacquisition. Add full LNAV almanac visibility scheduling, power-safe QSPI journaling, RF-domain reset isolation, and PPS Gray CDC hardening with Linux, sanitizer, RTL, and end-to-end coverage.
This commit is contained in:
onur
2026-08-04 02:52:35 +03:00
parent 347e1c671d
commit 30179df8e6
69 changed files with 6162 additions and 247 deletions
@@ -449,6 +449,7 @@ class APBGpsBlock(params: FpgaGPSParams, beatBytes: Int)(implicit p: Parameters)
val trackingClockNode = BundleBridgeSink[Clock]()
val afeClockNode = BundleBridgeSink[Clock]()
val rfDataNode = BundleBridgeSink[UInt]()
val clockReadyNode = BundleBridgeSink[Bool]()
val ppsOutNode = BundleBridgeSource(() => Bool())
lazy val module = new Impl
@@ -464,21 +465,38 @@ class APBGpsBlock(params: FpgaGPSParams, beatBytes: Int)(implicit p: Parameters)
// GDB ndmreset reaches this reset. Deassert it inside each GPS clock domain so
// APB, acquisition, tracking, and RF/AFE logic do not leave reset on an async edge.
val gpsDomainReset = reset.asBool || !clockReadyNode.bundle
// Keep the APB/fabric side alive when the external RF clock disappears so
// the CPU and its RAM ephemeris cache survive. Only the clock-dependent GPS
// DSP domains receive the lock-qualified local reset.
val apbReset = ResetCatchAndSync(clock, reset.asBool, 3, "gps_apb_reset")
val acqReset = ResetCatchAndSync(acqClockNode.bundle, reset.asBool, 3, "gps_acq_reset")
val trckReset = ResetCatchAndSync(trackingClockNode.bundle, reset.asBool, 3, "gps_trck_reset")
val afeReset = ResetCatchAndSync(afeClockNode.bundle, reset.asBool, 3, "gps_afe_reset")
val acqReset = ResetCatchAndSync(acqClockNode.bundle, gpsDomainReset, 3, "gps_acq_reset")
val trckReset = ResetCatchAndSync(trackingClockNode.bundle, gpsDomainReset, 3, "gps_trck_reset")
val afeReset = ResetCatchAndSync(afeClockNode.bundle, gpsDomainReset, 3, "gps_afe_reset")
// PPS uses the fabric-derived tracking clock but must keep counting during
// RF/MMCM loss. Only a real SoC reset may reset its monotonic timebase.
val ppsReset = ResetCatchAndSync(trackingClockNode.bundle, reset.asBool, 3, "gps_pps_reset")
val apbResetn = !apbReset
val acqResetn = !acqReset
val trckResetn = !trckReset
val afeResetn = !afeReset
val ppsResetn = !ppsReset
afe.io.afe_rstn := afeResetn
afe.io.trk_rstn := trckResetn
afe.io.afe_clk := afeClockNode.bundle
afe.io.trk_clk := trackingClockNode.bundle
afe.io.start_i := acq.io.afe_start
// A disappearing RF clock resets the AFE while the APB register bank is
// intentionally retained. Generate one local restart pulse when the RF
// domain returns; the normal software pulse remains valid at cold boot.
val afeAutoStart = withClockAndReset(afeClockNode.bundle, afeReset) {
val started = RegInit(false.B)
val pulse = !started
started := true.B
pulse
}
afe.io.start_i := acq.io.afe_start || afeAutoStart
afe.io.sample_counter_clear_i := acq.io.sample_counter_clear
acq.io.PCLK := clock
@@ -523,7 +541,7 @@ class APBGpsBlock(params: FpgaGPSParams, beatBytes: Int)(implicit p: Parameters)
pps.io.PWRITE := apb.pwrite
pps.io.PWDATA := apb.pwdata(31, 0)
pps.io.pps_clk := trackingClockNode.bundle
pps.io.pps_resetn := trckResetn
pps.io.pps_resetn := ppsResetn
pps.io.capture_i := VecInit(tracking.map(_.io.pps_capture)).asUInt
ppsOutNode.bundle := pps.io.pps_out
@@ -599,6 +617,7 @@ class VCU118RocketSystem(implicit p: Parameters) extends RocketSubsystem
val gpsTrackingClockSourceNode = p(VCU118GPSKey).map(_ => BundleBridgeSource(() => Clock()))
val gpsRfClockSourceNode = p(VCU118GPSKey).map(_ => BundleBridgeSource(() => Clock()))
val gpsRfDataSourceNode = p(VCU118GPSKey).map(_ => BundleBridgeSource(() => UInt(4.W)))
val gpsClockReadySourceNode = p(VCU118GPSKey).map(_ => BundleBridgeSource(() => Bool()))
private val pmodIRQDevice = new SimpleDevice("pmod-sensor-irq", Seq("rocket,pmod-sensor-irq"))
val pmodIRQIntNode = IntSourceNode(Seq(IntSourcePortParameters(Seq(
IntSourceParameters(IntRange(0, 1), pmodIRQDevice.int)))))
@@ -680,6 +699,7 @@ class VCU118RocketSystem(implicit p: Parameters) extends RocketSubsystem
gpsTrackingClockSourceNode.foreach { source => dev.trackingClockNode := source }
gpsRfClockSourceNode.foreach { source => dev.afeClockNode := source }
gpsRfDataSourceNode.foreach { source => dev.rfDataNode := source }
gpsClockReadySourceNode.foreach { source => dev.clockReadyNode := source }
dev
}
val gpsPPSSinkNode = gps.map(_.ppsOutNode.makeSink())
@@ -714,6 +734,7 @@ class VCU118RocketSystemModuleImp[+L <: VCU118RocketSystem](_outer: L)
val gps_trck_clk = _outer.gpsTrackingClockSourceNode.map(_.makeIO("gps_trck_clk"))
val gps_rf_clk = _outer.gpsRfClockSourceNode.map(_.makeIO("gps_rf_clk"))
val gps_rf_data = _outer.gpsRfDataSourceNode.map(_.makeIO("gps_rf_data"))
val gps_clock_ready = _outer.gpsClockReadySourceNode.map(_.makeIO("gps_clock_ready"))
val gps_pps = _outer.gpsPPSSinkNode.map(_.makeIO("gps_pps"))
_outer.pmodIRQIntNode.out.foreach { case (out, _) =>