Fix GPS nav epoch recovery and document debug levels
This commit is contained in:
@@ -2,7 +2,8 @@ CC ?= gcc
|
||||
CFLAGS ?= -O2 -std=c11 -Wall -Wextra -Werror
|
||||
|
||||
ROOT := ..
|
||||
TARGETS := test_imu_calibration test_gps_nav_epoch test_plic_warm_reset
|
||||
TARGETS := test_imu_calibration test_gps_nav_epoch \
|
||||
test_gps_nav_epoch_coalescing test_plic_warm_reset
|
||||
|
||||
.PHONY: all run clean
|
||||
|
||||
@@ -14,13 +15,17 @@ test_imu_calibration: test_imu_calibration.c $(ROOT)/src/imu/imu_calibration.c
|
||||
test_gps_nav_epoch: test_gps_nav_epoch.c $(ROOT)/src/gps/gps_nav_epoch.c
|
||||
$(CC) $(CFLAGS) -I$(ROOT)/header $^ -lm -o $@
|
||||
|
||||
test_gps_nav_epoch_coalescing: test_gps_nav_epoch_coalescing.c $(ROOT)/src/gps/gps_nav_epoch.c
|
||||
$(CC) $(CFLAGS) -I$(ROOT)/header $^ -lm -o $@
|
||||
|
||||
test_plic_warm_reset: test_plic_warm_reset.c $(ROOT)/../bsp/drivers/plic.c
|
||||
$(CC) $(CFLAGS) -Ifake_bsp -I$(ROOT)/../bsp/include $^ -o $@
|
||||
|
||||
run: $(TARGETS)
|
||||
./test_imu_calibration
|
||||
./test_gps_nav_epoch
|
||||
./test_gps_nav_epoch_coalescing
|
||||
./test_plic_warm_reset
|
||||
|
||||
clean:
|
||||
rm -f $(TARGETS)
|
||||
rm -f $(TARGETS) test_gps_nav_epoch_coalescing_repro
|
||||
|
||||
@@ -97,10 +97,12 @@ static void assembly_handles_duplicates_and_epoch_order(void)
|
||||
GPS_NAV_EPOCH_EVENT_STALE,
|
||||
"stale epoch interrupt was not rejected");
|
||||
CHECK(gps_nav_epoch_assembly_push(&assembly, 2001u, 1u, 0x0fu) ==
|
||||
GPS_NAV_EPOCH_EVENT_FUTURE,
|
||||
"future epoch was mixed into an incomplete epoch");
|
||||
CHECK(assembly.received_mask == 0x01u,
|
||||
"rejected interrupts changed the received mask");
|
||||
GPS_NAV_EPOCH_EVENT_RESTARTED,
|
||||
"newer epoch did not replace the incomplete epoch");
|
||||
CHECK((assembly.epoch_id == 2001u) &&
|
||||
(assembly.expected_mask == 0x0fu) &&
|
||||
(assembly.received_mask == 0x02u),
|
||||
"newer epoch restart retained measurements from the old epoch");
|
||||
}
|
||||
|
||||
static void assembly_disconnect_and_reconnect(void)
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
#include "gps/gps_nav_epoch.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define CHANNEL_COUNT 6u
|
||||
#define READY_MASK 0x3fu
|
||||
#define MISSING_CH 4u
|
||||
|
||||
typedef struct {
|
||||
bool pending;
|
||||
uint32_t capture_sequence;
|
||||
uint64_t mailbox_epoch;
|
||||
} synthetic_tracking_irq_t;
|
||||
|
||||
static int failures;
|
||||
|
||||
#define CHECK(condition, message) do { \
|
||||
if(!(condition)) { \
|
||||
fprintf(stderr, "FAIL: %s\n", message); \
|
||||
failures++; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/*
|
||||
* The tracking interrupt source is a sticky pending bit, while the associated
|
||||
* measurement registers are a one-entry mailbox. A second pulse therefore
|
||||
* advances the PPS capture sequence and overwrites the mailbox even when the
|
||||
* first pulse has not yet been serviced.
|
||||
*/
|
||||
static void hardware_nav_pulse(
|
||||
synthetic_tracking_irq_t channels[CHANNEL_COUNT],
|
||||
uint64_t epoch)
|
||||
{
|
||||
for(unsigned int channel = 0u ; channel < CHANNEL_COUNT ; channel++)
|
||||
{
|
||||
channels[channel].capture_sequence++;
|
||||
channels[channel].mailbox_epoch = epoch;
|
||||
channels[channel].pending = true;
|
||||
}
|
||||
}
|
||||
|
||||
static gps_nav_epoch_event_result_t service_channel(
|
||||
synthetic_tracking_irq_t channels[CHANNEL_COUNT],
|
||||
uint32_t serviced_sequence[CHANNEL_COUNT],
|
||||
gps_nav_epoch_assembly_t* assembly,
|
||||
unsigned int channel,
|
||||
unsigned int* coalesced_pulses)
|
||||
{
|
||||
if((channel >= CHANNEL_COUNT) || !channels[channel].pending)
|
||||
{
|
||||
return GPS_NAV_EPOCH_EVENT_INVALID;
|
||||
}
|
||||
|
||||
uint32_t sequence_delta =
|
||||
channels[channel].capture_sequence - serviced_sequence[channel];
|
||||
if((serviced_sequence[channel] != 0u) && (sequence_delta > 1u))
|
||||
{
|
||||
*coalesced_pulses += sequence_delta - 1u;
|
||||
}
|
||||
serviced_sequence[channel] = channels[channel].capture_sequence;
|
||||
channels[channel].pending = false;
|
||||
|
||||
return gps_nav_epoch_assembly_push(
|
||||
assembly, channels[channel].mailbox_epoch, channel, READY_MASK);
|
||||
}
|
||||
|
||||
static void service_complete_epoch(
|
||||
synthetic_tracking_irq_t channels[CHANNEL_COUNT],
|
||||
uint32_t serviced_sequence[CHANNEL_COUNT],
|
||||
gps_nav_epoch_assembly_t* assembly,
|
||||
unsigned int* coalesced_pulses)
|
||||
{
|
||||
for(unsigned int channel = 0u ; channel < CHANNEL_COUNT ; channel++)
|
||||
{
|
||||
gps_nav_epoch_event_result_t result =
|
||||
service_channel(channels, serviced_sequence, assembly,
|
||||
channel, coalesced_pulses);
|
||||
CHECK((result == GPS_NAV_EPOCH_EVENT_COMPLETE) ==
|
||||
(channel == CHANNEL_COUNT - 1u),
|
||||
"healthy epoch did not complete on its final channel");
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
synthetic_tracking_irq_t channels[CHANNEL_COUNT] = {0};
|
||||
uint32_t serviced_sequence[CHANNEL_COUNT] = {0};
|
||||
gps_nav_epoch_assembly_t assembly;
|
||||
unsigned int coalesced_pulses = 0u;
|
||||
unsigned int completed_epochs = 0u;
|
||||
const uint64_t baseline_epoch = 1151141640u;
|
||||
const uint64_t blocked_epoch = 1151141641u;
|
||||
|
||||
/* Establish the same non-zero PPS sequence baseline as the running board. */
|
||||
gps_nav_epoch_assembly_reset(&assembly);
|
||||
hardware_nav_pulse(channels, baseline_epoch);
|
||||
service_complete_epoch(channels, serviced_sequence, &assembly,
|
||||
&coalesced_pulses);
|
||||
completed_epochs++;
|
||||
gps_nav_epoch_assembly_reset(&assembly);
|
||||
|
||||
/* All six pulse, but the CPU services every source except tracking 4. */
|
||||
hardware_nav_pulse(channels, blocked_epoch);
|
||||
for(unsigned int channel = 0u ; channel < CHANNEL_COUNT ; channel++)
|
||||
{
|
||||
if(channel == MISSING_CH)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
CHECK(service_channel(channels, serviced_sequence, &assembly,
|
||||
channel, &coalesced_pulses) ==
|
||||
GPS_NAV_EPOCH_EVENT_ACCEPTED,
|
||||
"partial blocked epoch was not accepted");
|
||||
}
|
||||
CHECK(assembly.expected_mask == 0x3fu,
|
||||
"blocked epoch expected mask differs from the board");
|
||||
CHECK(assembly.received_mask == 0x2fu,
|
||||
"blocked epoch received mask differs from the board");
|
||||
|
||||
/*
|
||||
* Before channel 4 is serviced, the next hardware pulse overwrites its
|
||||
* one-entry mailbox. Its sequence delta is two, but only the newer epoch
|
||||
* can be delivered to the assembler.
|
||||
*/
|
||||
hardware_nav_pulse(channels, blocked_epoch + 1u);
|
||||
for(unsigned int channel = 0u ; channel < CHANNEL_COUNT ; channel++)
|
||||
{
|
||||
gps_nav_epoch_event_result_t result =
|
||||
service_channel(channels, serviced_sequence, &assembly,
|
||||
channel, &coalesced_pulses);
|
||||
gps_nav_epoch_event_result_t expected =
|
||||
(channel == 0u) ? GPS_NAV_EPOCH_EVENT_RESTARTED :
|
||||
(channel == CHANNEL_COUNT - 1u) ?
|
||||
GPS_NAV_EPOCH_EVENT_COMPLETE :
|
||||
GPS_NAV_EPOCH_EVENT_ACCEPTED;
|
||||
CHECK(result == expected,
|
||||
"newer hardware epoch did not restart and complete cleanly");
|
||||
}
|
||||
CHECK(coalesced_pulses == 1u,
|
||||
"PPS sequence did not expose the overwritten channel-4 pulse");
|
||||
CHECK((assembly.epoch_id == blocked_epoch + 1u) &&
|
||||
(assembly.expected_mask == READY_MASK) &&
|
||||
(assembly.received_mask == READY_MASK) &&
|
||||
gps_nav_epoch_assembly_complete(&assembly),
|
||||
"assembler did not recover on the first complete newer epoch");
|
||||
completed_epochs++;
|
||||
|
||||
/* Every later healthy epoch must continue to complete normally. */
|
||||
for(uint64_t offset = 2u ; offset < 102u ; offset++)
|
||||
{
|
||||
gps_nav_epoch_assembly_reset(&assembly);
|
||||
hardware_nav_pulse(channels, blocked_epoch + offset);
|
||||
service_complete_epoch(channels, serviced_sequence, &assembly,
|
||||
&coalesced_pulses);
|
||||
completed_epochs++;
|
||||
}
|
||||
|
||||
CHECK(completed_epochs == 102u,
|
||||
"recovery stream lost or duplicated a completed epoch");
|
||||
|
||||
if(failures == 0)
|
||||
{
|
||||
printf("RECOVERED: active=%llu expected=0x%x received=0x%x "
|
||||
"coalesced=%u completed_epochs=%u\n",
|
||||
(unsigned long long)assembly.epoch_id,
|
||||
assembly.expected_mask,
|
||||
assembly.received_mask,
|
||||
coalesced_pulses,
|
||||
completed_epochs);
|
||||
}
|
||||
return failures == 0 ? 0 : 1;
|
||||
}
|
||||
Reference in New Issue
Block a user