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:
+23
-3
@@ -67,6 +67,8 @@ module tracking_wrapper #(
|
||||
// nav solve timer I/O
|
||||
logic nav_solve_timer_reached;
|
||||
logic nav_solve_timer_load_abs_sample_signal;
|
||||
logic nav_solve_phase_seed_valid;
|
||||
logic [31:0] nav_solve_phase_seed;
|
||||
|
||||
|
||||
//
|
||||
@@ -117,6 +119,7 @@ module tracking_wrapper #(
|
||||
logic [31 : 0] control_reg_p, control_reg_n;
|
||||
logic [31 : 0] timeout_reg_p, timeout_reg_n;
|
||||
logic [31 : 0] nav_solve_timer_reg_p, nav_solve_timer_reg_n;
|
||||
logic [31 : 0] nav_solve_phase_reg_p, nav_solve_phase_reg_n;
|
||||
logic [30 : 0] nav_bit_read_reg_p[10], nav_bit_read_reg_n[10];
|
||||
|
||||
// Atomic APB-domain copy of the 20 ms measurement.
|
||||
@@ -143,9 +146,9 @@ module tracking_wrapper #(
|
||||
|
||||
assign to_start_trck_pin = (start_trck_ctl_p != 0) ? 1 : 0;
|
||||
assign to_stop_trck_pin = (stop_trck_ctl_p != 0) ? 1 : 0;
|
||||
// ABS_SAMPLE and REM_CODE_PHASE are latched on this same PCLK pulse. PPS
|
||||
// timestamps only navigation-solve epochs; subframe-ready is not a position
|
||||
// epoch and therefore must not create a PPS anchor.
|
||||
// This pulse and the measurement payload originate on the same 125 MHz
|
||||
// tracking epoch. PPS latches it natively; the payload uses the bundled-data
|
||||
// PCLK crossing below. Subframe-ready is not a position/PPS epoch.
|
||||
assign pps_capture = nav_solve_timer_reached;
|
||||
|
||||
// comb. logic register cdc
|
||||
@@ -363,6 +366,7 @@ module tracking_wrapper #(
|
||||
intr_pending_p <= '0;
|
||||
timeout_reg_p <= trck_pkg::TRCK_TIMEOUT_RESET_VAL;
|
||||
nav_solve_timer_reg_p <= 'ha;
|
||||
nav_solve_phase_reg_p <= '0;
|
||||
timeout_counter_p <= '0;
|
||||
start_trck_ctl_p <= '0;
|
||||
stop_trck_ctl_p <= '0;
|
||||
@@ -395,6 +399,7 @@ module tracking_wrapper #(
|
||||
intr_pending_p <= intr_pending_n;
|
||||
timeout_reg_p <= timeout_reg_n;
|
||||
nav_solve_timer_reg_p <= nav_solve_timer_reg_n;
|
||||
nav_solve_phase_reg_p <= nav_solve_phase_reg_n;
|
||||
timeout_counter_p <= timeout_counter_n;
|
||||
start_trck_ctl_p <= start_trck_ctl_n;
|
||||
stop_trck_ctl_p <= stop_trck_ctl_n;
|
||||
@@ -631,6 +636,8 @@ module tracking_wrapper #(
|
||||
.resetn(PRESETN), // active low reset.
|
||||
.enable(control_reg_p[TRCK_NAV_SOLVE_ENABLE_POS]), // timer enable bit.
|
||||
.counter_max_val(nav_solve_timer_reg_p), // timer generates interrupt when internal timer reaches this value.
|
||||
.phase_seed_valid(nav_solve_phase_seed_valid),
|
||||
.phase_seed(nav_solve_phase_seed),
|
||||
.bit_valid(bit_valid_syned), // timer counts bit valid signals.
|
||||
.subframe_ready(load_sigs), // signal that asserts 1 clock cycle after a subframe bit reached ( in essence, 300 bit + 8 preamble bit !!!)
|
||||
.subframe_count(how_z_count), // HOW Z-count of this globally identified subframe boundary.
|
||||
@@ -656,6 +663,9 @@ module tracking_wrapper #(
|
||||
control_reg_n = control_reg_p;
|
||||
timeout_reg_n = timeout_reg_p;
|
||||
nav_solve_timer_reg_n = nav_solve_timer_reg_p;
|
||||
nav_solve_phase_reg_n = nav_solve_phase_reg_p;
|
||||
nav_solve_phase_seed_valid = 1'b0;
|
||||
nav_solve_phase_seed = nav_solve_phase_reg_p;
|
||||
|
||||
if(apb_bus_ps == APB_STATE_SEL) begin
|
||||
if(PWRITE) begin // write to register
|
||||
@@ -715,6 +725,12 @@ module tracking_wrapper #(
|
||||
begin
|
||||
nav_solve_timer_reg_n = PWDATA;
|
||||
end
|
||||
(LOW_ADDRESS + NAV_SOLVE_PHASE_REG_OFFSET):
|
||||
begin
|
||||
nav_solve_phase_reg_n = PWDATA;
|
||||
nav_solve_phase_seed = PWDATA;
|
||||
nav_solve_phase_seed_valid = 1'b1;
|
||||
end
|
||||
|
||||
|
||||
default:
|
||||
@@ -827,6 +843,10 @@ module tracking_wrapper #(
|
||||
begin
|
||||
PRDATA = nav_solve_timer_reg_p;
|
||||
end
|
||||
(LOW_ADDRESS + NAV_SOLVE_PHASE_REG_OFFSET):
|
||||
begin
|
||||
PRDATA = nav_solve_phase_reg_p;
|
||||
end
|
||||
(LOW_ADDRESS + REM_CODE_PH_REG_OFFSET):
|
||||
begin
|
||||
//PRDATA = rem_code_ph_reg_p;
|
||||
|
||||
@@ -21,6 +21,7 @@ package trck_pkg;
|
||||
NAV_DATA_9_REG_OFFSET = 60,
|
||||
TRCK_TIMEOUT_REG_OFFSET = 64,
|
||||
NAV_SOLVE_TIMER_REG_OFFSET = 68,
|
||||
NAV_SOLVE_PHASE_REG_OFFSET = 72,
|
||||
REM_CODE_PH_REG_OFFSET = 76,
|
||||
MEASUREMENT_META_REG_OFFSET = 80,
|
||||
MEASUREMENT_SEQUENCE_REG_OFFSET = 84,
|
||||
|
||||
+16
-1
@@ -4,6 +4,8 @@ module nav_solve_timer(
|
||||
input logic resetn,
|
||||
input logic enable,
|
||||
input logic [31 : 0] counter_max_val,
|
||||
input logic phase_seed_valid,
|
||||
input logic [31 : 0] phase_seed,
|
||||
input logic bit_valid,
|
||||
input logic subframe_ready,
|
||||
input logic [16 : 0] subframe_count,
|
||||
@@ -84,6 +86,18 @@ module nav_solve_timer(
|
||||
phase_bit_n = 5'd25;
|
||||
nav_solve_state_n = nav_solve_timer_pkg::ALIGN;
|
||||
end
|
||||
/* A cached, week-qualified HOW/sample anchor lets software identify the
|
||||
* global navigation-bit phase from the first coherent 20 ms tracking
|
||||
* measurement. This explicit seed starts COUNT without waiting as long
|
||||
* as six seconds for another complete LNAV subframe. */
|
||||
if(phase_seed_valid && enable && trck_busy &&
|
||||
(counter_max_val != 0) && (phase_seed < counter_max_val)) begin
|
||||
internal_counter_n = phase_seed;
|
||||
phase_dividend_n = '0;
|
||||
phase_remainder_n = '0;
|
||||
phase_bit_n = '0;
|
||||
nav_solve_state_n = nav_solve_timer_pkg::COUNT;
|
||||
end
|
||||
if(nav_solve_state_p == nav_solve_timer_pkg::ALIGN) begin
|
||||
if(!enable || !trck_busy || (counter_max_val == 0)) begin
|
||||
nav_solve_state_n = nav_solve_timer_pkg::IDLE;
|
||||
@@ -114,7 +128,8 @@ module nav_solve_timer(
|
||||
internal_counter_n = (internal_counter_p == (counter_max_val - 1)) ? 0 : (internal_counter_p + 1);
|
||||
end
|
||||
end
|
||||
if(nav_solve_state_p == nav_solve_timer_pkg::IDLE) begin
|
||||
if((nav_solve_state_p == nav_solve_timer_pkg::IDLE) &&
|
||||
!phase_seed_valid) begin
|
||||
internal_counter_n = '0;
|
||||
end
|
||||
|
||||
|
||||
@@ -5,6 +5,10 @@ module nav_solve_timer_tb;
|
||||
logic resetn = 1'b0;
|
||||
logic enable = 1'b1;
|
||||
logic [31:0] counter_max_val;
|
||||
logic phase_seed_valid_a = 1'b0;
|
||||
logic phase_seed_valid_b = 1'b0;
|
||||
logic [31:0] phase_seed_a = '0;
|
||||
logic [31:0] phase_seed_b = '0;
|
||||
logic bit_valid = 1'b0;
|
||||
logic subframe_ready_a = 1'b0;
|
||||
logic subframe_ready_b = 1'b0;
|
||||
@@ -22,7 +26,9 @@ module nav_solve_timer_tb;
|
||||
always #5 clk = ~clk;
|
||||
|
||||
nav_solve_timer timer_a (
|
||||
.clk, .resetn, .enable, .counter_max_val, .bit_valid,
|
||||
.clk, .resetn, .enable, .counter_max_val,
|
||||
.phase_seed_valid(phase_seed_valid_a), .phase_seed(phase_seed_a),
|
||||
.bit_valid,
|
||||
.subframe_ready(subframe_ready_a),
|
||||
.subframe_count(subframe_count_a),
|
||||
.trck_busy, .nav_solve_timer_reached(reached_a),
|
||||
@@ -30,7 +36,9 @@ module nav_solve_timer_tb;
|
||||
);
|
||||
|
||||
nav_solve_timer timer_b (
|
||||
.clk, .resetn, .enable, .counter_max_val, .bit_valid,
|
||||
.clk, .resetn, .enable, .counter_max_val,
|
||||
.phase_seed_valid(phase_seed_valid_b), .phase_seed(phase_seed_b),
|
||||
.bit_valid,
|
||||
.subframe_ready(subframe_ready_b),
|
||||
.subframe_count(subframe_count_b),
|
||||
.trck_busy, .nav_solve_timer_reached(reached_b),
|
||||
@@ -96,11 +104,36 @@ module nav_solve_timer_tb;
|
||||
repeat(period * 3) pulse_nav_bit();
|
||||
endtask
|
||||
|
||||
task automatic run_cached_seed_scenario(input int unsigned period,
|
||||
input int unsigned start_bit);
|
||||
counter_max_val = period;
|
||||
global_bit = start_bit;
|
||||
b_active = 1'b1;
|
||||
resetn = 1'b0;
|
||||
clock_cycles(3);
|
||||
resetn = 1'b1;
|
||||
clock_cycles(2);
|
||||
|
||||
/* Software observed start_bit's already-consumed measurement and writes
|
||||
* its global bit phase. No subframe_ready pulse is provided. */
|
||||
phase_seed_a = start_bit % period;
|
||||
phase_seed_b = start_bit % period;
|
||||
phase_seed_valid_a = 1'b1;
|
||||
phase_seed_valid_b = 1'b1;
|
||||
@(posedge clk);
|
||||
@(negedge clk);
|
||||
phase_seed_valid_a = 1'b0;
|
||||
phase_seed_valid_b = 1'b0;
|
||||
repeat(period * 3) pulse_nav_bit();
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
run_late_join_scenario(40);
|
||||
run_late_join_scenario(37);
|
||||
run_late_join_scenario(512);
|
||||
run_late_join_scenario(1);
|
||||
run_cached_seed_scenario(10, 30007);
|
||||
run_cached_seed_scenario(37, 30111);
|
||||
$display("nav_solve_timer_tb PASS");
|
||||
$finish;
|
||||
end
|
||||
|
||||
+36
-13
@@ -17,6 +17,24 @@ 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.
|
||||
|
||||
At cold power-on the raw counter resets to zero and immediately increments on
|
||||
every 125 MHz clock, even with no antenna signal. It is therefore a valid
|
||||
elapsed-time/capture source, but it has no GPS week/TOW meaning by itself.
|
||||
`STATUS.VALID`, lock, and the physical PPS pin remain suppressed until fresh
|
||||
LNAV time/ephemeris supports WLS, a coarse mapping is committed, and the lock
|
||||
qualification completes.
|
||||
|
||||
Position differential pseudorange uses the tracking-domain observation
|
||||
`ABS_SAMPLE - REM_CODE_PHASE/1.023e6`, not per-channel PPS capture-counter
|
||||
differences. The shared RF sample counter supplies the integer datum and the
|
||||
code-NCO residue removes its 61 ns integer-sample quantum. The 125 MHz raw
|
||||
counter remains the monotonic source for epoch assembly, GPS-time/PPS mailbox
|
||||
anchoring, discipline, and holdover. Thus PPS capture quantization and its CDC
|
||||
pipeline are not injected into XYZ WLS. RF sample-rate scale error still does
|
||||
not behave as a common WLS bias, but its effect is limited to the
|
||||
millisecond-scale inter-channel receive-time span rather than the duration of
|
||||
a tunnel outage.
|
||||
|
||||
`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
|
||||
@@ -59,11 +77,13 @@ 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.
|
||||
`b_r = c*delta_t_r` and `bdot_r = c*d(delta_t_r)/dt`. The position observations
|
||||
in this project instead subtract satellite transmit-time differences from
|
||||
wrap-safe corrected RF-sample differences. The fractional code residue removes
|
||||
the integer RF-sample quantum. No startup or nominal flight-time constant is
|
||||
added. The position-WLS bias is therefore approximately
|
||||
`-reference_range` plus the reference satellite clock correction, 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
|
||||
@@ -83,12 +103,14 @@ 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
|
||||
The `capture_i` vector is synchronous to `pps_clk`, which is the same 125 MHz
|
||||
clock used by every tracking channel. Each one-cycle pulse latches the native
|
||||
raw counter and increments that channel's source-domain sequence, so it cannot
|
||||
be missed or phase-quantized by the slower APB clock. The stable bundled
|
||||
counter crosses to PCLK first and its visible sequence is delayed one extra
|
||||
PCLK cycle; firmware's sequence/data/sequence read rejects any capture that
|
||||
changes during an APB transaction. `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))
|
||||
@@ -96,8 +118,9 @@ 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.
|
||||
The PPS timebase reset is independent of RF/MMCM readiness, so its raw counter,
|
||||
disciplined accumulator and holdover rate survive a tunnel or antenna-clock
|
||||
loss while acquisition/tracking DSP domains reset and restart.
|
||||
|
||||
## APB register map
|
||||
|
||||
|
||||
+58
-21
@@ -46,10 +46,18 @@ module pps_wrapper #(
|
||||
logic commit_overrun_p, commit_overrun_n;
|
||||
logic diag_req_toggle_p, diag_req_toggle_n;
|
||||
|
||||
logic [PPS_COUNTER_WIDTH-1:0] capture_counter_p [NUM_CAPTURE_CHANNELS];
|
||||
logic [PPS_COUNTER_WIDTH-1:0] capture_counter_n [NUM_CAPTURE_CHANNELS];
|
||||
logic [PPS_SEQUENCE_WIDTH-1:0] capture_sequence_p [NUM_CAPTURE_CHANNELS];
|
||||
logic [PPS_SEQUENCE_WIDTH-1:0] capture_sequence_n [NUM_CAPTURE_CHANNELS];
|
||||
logic [PPS_COUNTER_WIDTH-1:0] capture_counter_pps [NUM_CAPTURE_CHANNELS];
|
||||
logic [PPS_SEQUENCE_WIDTH-1:0] capture_sequence_pps [NUM_CAPTURE_CHANNELS];
|
||||
(* ASYNC_REG = "TRUE" *) logic [PPS_COUNTER_WIDTH-1:0]
|
||||
capture_counter_sync1_p [NUM_CAPTURE_CHANNELS];
|
||||
(* ASYNC_REG = "TRUE" *) logic [PPS_COUNTER_WIDTH-1:0]
|
||||
capture_counter_sync2_p [NUM_CAPTURE_CHANNELS];
|
||||
(* ASYNC_REG = "TRUE" *) logic [PPS_SEQUENCE_WIDTH-1:0]
|
||||
capture_sequence_sync1_p [NUM_CAPTURE_CHANNELS];
|
||||
(* ASYNC_REG = "TRUE" *) logic [PPS_SEQUENCE_WIDTH-1:0]
|
||||
capture_sequence_sync2_p [NUM_CAPTURE_CHANNELS];
|
||||
logic [PPS_SEQUENCE_WIDTH-1:0]
|
||||
capture_sequence_visible_p [NUM_CAPTURE_CHANNELS];
|
||||
logic [PPS_COUNTER_WIDTH-1:0] raw_snapshot_p, raw_snapshot_n;
|
||||
|
||||
logic [PPS_COUNTER_WIDTH-1:0] raw_counter_pps;
|
||||
@@ -93,17 +101,41 @@ module pps_wrapper #(
|
||||
logic apb_write_access;
|
||||
logic apb_read_access;
|
||||
logic [31:0] paddr_extended;
|
||||
integer channel_pps;
|
||||
integer channel_seq;
|
||||
integer channel_comb;
|
||||
|
||||
assign raw_counter_pclk = pps_gray_to_binary(raw_gray_sync2_p);
|
||||
assign raw_counter_gray_pps = raw_counter_pps ^ (raw_counter_pps >> 1);
|
||||
assign commit_busy = commit_req_toggle_p ^ commit_ack_sync2_p;
|
||||
assign diag_busy = diag_req_toggle_p ^ diag_ack_sync2_p;
|
||||
assign apb_write_access = (apb_bus_ps == PPS_APB_STATE_ACCESS) && PWRITE;
|
||||
assign apb_read_access = (apb_bus_ps == PPS_APB_STATE_ACCESS) && !PWRITE;
|
||||
assign paddr_extended = {{(32-ADDR_BUS_WIDTH){1'b0}}, PADDR};
|
||||
|
||||
/* Capture events are native to this same 125 MHz tracking/PPS domain. Latch
|
||||
* the counter here so an 8 ns event cannot be missed or quantized by PCLK.
|
||||
* Counter/sequence remain stable for an entire navigation-solve period; the
|
||||
* delayed visible sequence below is the bundled-data validity marker. */
|
||||
always_ff @(posedge pps_clk or negedge pps_resetn) begin
|
||||
if (!pps_resetn) begin
|
||||
raw_counter_gray_pps <= '0;
|
||||
for (channel_pps = 0; channel_pps < NUM_CAPTURE_CHANNELS;
|
||||
channel_pps = channel_pps + 1) begin
|
||||
capture_counter_pps[channel_pps] <= '0;
|
||||
capture_sequence_pps[channel_pps] <= '0;
|
||||
end
|
||||
end else begin
|
||||
raw_counter_gray_pps <= raw_counter_pps ^ (raw_counter_pps >> 1);
|
||||
for (channel_pps = 0; channel_pps < NUM_CAPTURE_CHANNELS;
|
||||
channel_pps = channel_pps + 1) begin
|
||||
if (capture_i[channel_pps]) begin
|
||||
capture_counter_pps[channel_pps] <= raw_counter_pps;
|
||||
capture_sequence_pps[channel_pps] <=
|
||||
capture_sequence_pps[channel_pps] + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge pps_clk or negedge pps_resetn) begin
|
||||
if (!pps_resetn) begin
|
||||
control_sync1_p <= '0;
|
||||
@@ -180,8 +212,11 @@ module pps_wrapper #(
|
||||
rate_word_sync1_p <= '0;
|
||||
rate_word_sync2_p <= '0;
|
||||
for (channel_seq = 0; channel_seq < NUM_CAPTURE_CHANNELS; channel_seq = channel_seq + 1) begin
|
||||
capture_counter_p[channel_seq] <= '0;
|
||||
capture_sequence_p[channel_seq] <= '0;
|
||||
capture_counter_sync1_p[channel_seq] <= '0;
|
||||
capture_counter_sync2_p[channel_seq] <= '0;
|
||||
capture_sequence_sync1_p[channel_seq] <= '0;
|
||||
capture_sequence_sync2_p[channel_seq] <= '0;
|
||||
capture_sequence_visible_p[channel_seq] <= '0;
|
||||
end
|
||||
end else begin
|
||||
apb_bus_ps <= apb_bus_ns;
|
||||
@@ -233,8 +268,18 @@ module pps_wrapper #(
|
||||
rate_word_sync1_p <= diag_rate_word_pps;
|
||||
rate_word_sync2_p <= rate_word_sync1_p;
|
||||
for (channel_seq = 0; channel_seq < NUM_CAPTURE_CHANNELS; channel_seq = channel_seq + 1) begin
|
||||
capture_counter_p[channel_seq] <= capture_counter_n[channel_seq];
|
||||
capture_sequence_p[channel_seq] <= capture_sequence_n[channel_seq];
|
||||
capture_counter_sync1_p[channel_seq] <=
|
||||
capture_counter_pps[channel_seq];
|
||||
capture_counter_sync2_p[channel_seq] <=
|
||||
capture_counter_sync1_p[channel_seq];
|
||||
capture_sequence_sync1_p[channel_seq] <=
|
||||
capture_sequence_pps[channel_seq];
|
||||
capture_sequence_sync2_p[channel_seq] <=
|
||||
capture_sequence_sync1_p[channel_seq];
|
||||
/* Publish sequence one PCLK later than the payload. Firmware reads
|
||||
* sequence/data/sequence and therefore never accepts a mixed capture. */
|
||||
capture_sequence_visible_p[channel_seq] <=
|
||||
capture_sequence_sync2_p[channel_seq];
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -266,14 +311,6 @@ module pps_wrapper #(
|
||||
commit_overrun_n = commit_overrun_p;
|
||||
diag_req_toggle_n = diag_req_toggle_p;
|
||||
raw_snapshot_n = raw_snapshot_p;
|
||||
for (channel_comb = 0; channel_comb < NUM_CAPTURE_CHANNELS; channel_comb = channel_comb + 1) begin
|
||||
capture_counter_n[channel_comb] = capture_counter_p[channel_comb];
|
||||
capture_sequence_n[channel_comb] = capture_sequence_p[channel_comb];
|
||||
if (capture_i[channel_comb]) begin
|
||||
capture_counter_n[channel_comb] = raw_counter_pclk;
|
||||
capture_sequence_n[channel_comb] = capture_sequence_p[channel_comb] + 32'd1;
|
||||
end
|
||||
end
|
||||
|
||||
if (apb_read_access && (paddr_extended == (LOW_ADDRESS + PPS_RAW_COUNTER_LO_REG_OFFSET)))
|
||||
raw_snapshot_n = raw_counter_pclk;
|
||||
@@ -388,9 +425,9 @@ module pps_wrapper #(
|
||||
PRDATA = '0;
|
||||
PRDATA[CAPTURE_SEL_WIDTH-1:0] = capture_select_p;
|
||||
end
|
||||
(LOW_ADDRESS + PPS_CAPTURE_COUNTER_LO_OFFSET): PRDATA = capture_counter_p[capture_select_p][31:0];
|
||||
(LOW_ADDRESS + PPS_CAPTURE_COUNTER_HI_OFFSET): PRDATA = capture_counter_p[capture_select_p][63:32];
|
||||
(LOW_ADDRESS + PPS_CAPTURE_SEQUENCE_OFFSET): PRDATA = capture_sequence_p[capture_select_p];
|
||||
(LOW_ADDRESS + PPS_CAPTURE_COUNTER_LO_OFFSET): PRDATA = capture_counter_sync2_p[capture_select_p][31:0];
|
||||
(LOW_ADDRESS + PPS_CAPTURE_COUNTER_HI_OFFSET): PRDATA = capture_counter_sync2_p[capture_select_p][63:32];
|
||||
(LOW_ADDRESS + PPS_CAPTURE_SEQUENCE_OFFSET): PRDATA = capture_sequence_visible_p[capture_select_p];
|
||||
(LOW_ADDRESS + PPS_EPOCH_COUNTER_LO_OFFSET): PRDATA = epoch_counter_shadow_p[31:0];
|
||||
(LOW_ADDRESS + PPS_EPOCH_COUNTER_HI_OFFSET): PRDATA = epoch_counter_shadow_p[63:32];
|
||||
(LOW_ADDRESS + PPS_GPS_SECONDS_LO_OFFSET): PRDATA = gps_seconds_shadow_p[31:0];
|
||||
|
||||
+15
-9
@@ -114,6 +114,15 @@ module pps_wrapper_tb;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic pulse_native_capture(input int unsigned channel_index);
|
||||
begin
|
||||
/* Real tracking capture is exactly one shared 125 MHz PPS-clock pulse,
|
||||
* potentially narrower than a PCLK period. */
|
||||
@(negedge pps_clk); capture_i[channel_index] = 1'b1;
|
||||
@(negedge pps_clk); capture_i[channel_index] = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic wait_commit_idle;
|
||||
logic [31:0] status;
|
||||
int timeout;
|
||||
@@ -238,9 +247,8 @@ module pps_wrapper_tb;
|
||||
check_true((dut.i_pps_core.raw_counter - raw_snapshot_value) < 100,
|
||||
"raw-counter low-read snapshot keeps the high word coherent");
|
||||
|
||||
@(negedge PCLK); capture_i[1] = 1'b1;
|
||||
@(negedge PCLK); capture_i[1] = 1'b0;
|
||||
repeat (3) @(posedge PCLK);
|
||||
pulse_native_capture(1);
|
||||
repeat (5) @(posedge PCLK);
|
||||
read_capture(1, capture_counter, capture_sequence_id);
|
||||
check_true(capture_sequence_id == 1, "channel capture sequence increments");
|
||||
check_true(capture_counter != 0, "channel capture stores the free-running counter");
|
||||
@@ -311,9 +319,8 @@ module pps_wrapper_tb;
|
||||
|
||||
// Build the next navigation reference from a new captured hardware epoch.
|
||||
repeat (40) @(posedge pps_clk);
|
||||
@(negedge PCLK); capture_i[2] = 1'b1;
|
||||
@(negedge PCLK); capture_i[2] = 1'b0;
|
||||
repeat (3) @(posedge PCLK);
|
||||
pulse_native_capture(2);
|
||||
repeat (5) @(posedge PCLK);
|
||||
read_capture(2, second_capture, second_capture_sequence);
|
||||
check_true(second_capture_sequence == 1, "independent capture bank per tracking channel");
|
||||
phase_ticks = (second_capture - pps_edge_counter) + 2;
|
||||
@@ -346,9 +353,8 @@ module pps_wrapper_tb;
|
||||
"new diagnostic snapshot advances the observed time");
|
||||
|
||||
// Replaying sequence 7 must acknowledge the mailbox but not update PI.
|
||||
@(negedge PCLK); capture_i[2] = 1'b1;
|
||||
@(negedge PCLK); capture_i[2] = 1'b0;
|
||||
repeat (3) @(posedge PCLK);
|
||||
pulse_native_capture(2);
|
||||
repeat (5) @(posedge PCLK);
|
||||
read_capture(2, second_capture, second_capture_sequence);
|
||||
check_true(second_capture_sequence == 2, "repeated channel capture advances its sequence");
|
||||
pps_sw_commit(second_capture, 102, ticks_to_fraction_q32(phase_ticks), 7, 1'b0);
|
||||
|
||||
+4
-2
@@ -105,8 +105,10 @@ gelir. Bu ortak state olmadan oscillator hatasi receiver hareketinin bir
|
||||
parcasi sanilirdi.
|
||||
|
||||
Bu projedeki position WLS bias'i ise mutlak receiver clock bias'i degildir.
|
||||
Ortak capture zamani cikarildigi ve `STARTOFFSET` eklendigi icin
|
||||
referans-uyduya bagli goreli bir datum cozer. Bu nedenle Doppler WLS
|
||||
Wrap-safe corrected RF-sample farkindan satellite transmit-time farki
|
||||
cikarildigi icin, sabit startup offset'i olmadan referans-uyduya bagli goreli
|
||||
bir datum cozer. Fractional code residue integer RF-sample quantization'ini
|
||||
giderir. Bu nedenle Doppler WLS
|
||||
`clock_drift_mps` state'i, position WLS `clock_bias_m` degerinin turevi olarak
|
||||
dogrudan baglanmaz.
|
||||
|
||||
|
||||
@@ -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