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:
@@ -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, _) =>
|
||||
|
||||
Reference in New Issue
Block a user