diff --git a/config_d/registers b/config_d/registers index 752da7a..e108ec0 100755 --- a/config_d/registers +++ b/config_d/registers @@ -55,6 +55,10 @@ BLOCKS_COMPAT_VERSION = 0 # frequency of 125 MHz will be reported and used. NOMINAL_CLOCK 24 + # Hardware timestamps + PCAP_TS_SEC 25 + PCAP_TS_NSEC 26 + # These registers are used by the kernel driver to read the data capture stream. # This block is not used by the server, but is here for documentation and other diff --git a/server/data_server.c b/server/data_server.c index 8e00644..8427677 100644 --- a/server/data_server.c +++ b/server/data_server.c @@ -101,6 +101,7 @@ static const struct data_capture *data_capture; static struct timespec pcap_arm_ts; /* PCAP becomes armed & enabled timestamp */ static struct timespec pcap_start_ts; +static int64_t pcap_hw_ts_offset; /* Data completion code at end of experiment. */ static unsigned int completion_code; @@ -108,6 +109,30 @@ static unsigned int completion_code; static uint64_t experiment_sample_count; +/* Save start timestamp, either from hardware (if that timestamp is not 0), or + * from the driver (which was saved in the interrupt handler triggered by the + * start event) */ +static void update_start_timestamp(void) +{ + struct timespec pcap_drv_start_ts, pcap_hw_start_ts; + hw_get_start_ts(&pcap_drv_start_ts); + hw_get_hw_start_ts(&pcap_hw_start_ts); + if (pcap_hw_start_ts.tv_sec == 0 && pcap_hw_start_ts.tv_nsec == 0) { + pcap_start_ts = pcap_drv_start_ts; + pcap_hw_ts_offset = 0; + } else { + int64_t drv_ts_num = + (int64_t) pcap_drv_start_ts.tv_sec * 1000000000 + + pcap_drv_start_ts.tv_nsec; + int64_t hw_ts_num = + (int64_t) pcap_hw_start_ts.tv_sec * 1000000000 + + pcap_hw_start_ts.tv_nsec; + pcap_start_ts = pcap_hw_start_ts; + pcap_hw_ts_offset = drv_ts_num - hw_ts_num; + } +} + + /* Performs a complete experiment capture: start data buffer, process the data * stream until hardware is complete, stop data buffer. */ static void capture_experiment(void) @@ -135,7 +160,7 @@ static void capture_experiment(void) * timestamp, that's how it goes. */ if (!ts_captured) { - hw_get_start_ts(&pcap_start_ts); + update_start_timestamp(); ts_captured = true; } @@ -687,7 +712,7 @@ error__t process_data_socket(int scon) ok = send_data_header( captured_fields, data_capture, &connection.options, connection.file, lost_samples, - &pcap_arm_ts, &pcap_start_ts); + &pcap_arm_ts, &pcap_start_ts, pcap_hw_ts_offset); uint64_t sent_samples = 0; if (ok) diff --git a/server/hardware.c b/server/hardware.c index 96c18d1..251c5e8 100644 --- a/server/hardware.c +++ b/server/hardware.c @@ -324,6 +324,13 @@ void hw_get_start_ts(struct timespec *ts) ts->tv_nsec = (typeof(ts->tv_nsec)) compat_ts.tv_nsec; } + +void hw_get_hw_start_ts(struct timespec *ts) +{ + ts->tv_sec = (time_t) read_named_register(PCAP_TS_SEC); + ts->tv_nsec = (typeof(ts->tv_nsec)) read_named_register(PCAP_TS_NSEC); +} + #endif diff --git a/server/hardware.h b/server/hardware.h index bf4d2a0..2ee761b 100644 --- a/server/hardware.h +++ b/server/hardware.h @@ -166,6 +166,8 @@ const char *hw_decode_completion(unsigned int completion); /* This function gets the timestamp when PCAP becomes armed and enabled */ void hw_get_start_ts(struct timespec *ts); +/* This one is latched in hardware instead of the driver */ +void hw_get_hw_start_ts(struct timespec *ts); /* This function controls the arm/disarm state of data capture. Data capture is * armed by writing true with this function, after which hw_read_streamed_data() diff --git a/server/prepare.c b/server/prepare.c index 3496afa..f86f5ed 100644 --- a/server/prepare.c +++ b/server/prepare.c @@ -278,7 +278,7 @@ static void send_capture_info( struct buffered_file *file, const struct data_capture *capture, const struct data_options *options, uint64_t missed_samples, struct timespec *pcap_arm_tsp, - struct timespec *pcap_start_tsp) + struct timespec *pcap_start_tsp, int64_t pcap_hw_ts_offset) { static const char *data_format_strings[] = { [DATA_FORMAT_UNFRAMED] = "Unframed", @@ -303,6 +303,7 @@ static void send_capture_info( format_timestamp_message( timestamp_message, MAX_RESULT_LENGTH, pcap_start_tsp); format_attribute(&element, "start_time", "%s", timestamp_message); + format_attribute(&element, "hw_time_offset", "%"PRIi64, pcap_hw_ts_offset); format_attribute(&element, "missed", "%"PRIu64, missed_samples); format_attribute(&element, "process", "%s", data_process); format_attribute(&element, "format", "%s", data_format); @@ -377,13 +378,15 @@ bool send_data_header( const struct data_capture *capture, const struct data_options *options, struct buffered_file *file, uint64_t missed_samples, - struct timespec *pcap_arm_tsp, struct timespec *pcap_start_tsp) + struct timespec *pcap_arm_tsp, struct timespec *pcap_start_tsp, + int64_t pcap_hw_ts_offset) { struct xml_element header = start_element(file, "header", options->xml_header, true, true); send_capture_info( - file, capture, options, missed_samples, pcap_arm_tsp, pcap_start_tsp); + file, capture, options, missed_samples, pcap_arm_tsp, pcap_start_tsp, + pcap_hw_ts_offset); /* Format the field capture descriptions. */ struct xml_element field_group = diff --git a/server/prepare.h b/server/prepare.h index b75c26d..6f5e36d 100644 --- a/server/prepare.h +++ b/server/prepare.h @@ -46,7 +46,8 @@ bool send_data_header( const struct data_capture *capture, const struct data_options *options, struct buffered_file *file, uint64_t lost_samples, - struct timespec *pcap_arm_tsp, struct timespec *pcap_start_tsp); + struct timespec *pcap_arm_tsp, struct timespec *pcap_start_tsp, + int64_t pcap_hw_ts_offset); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ diff --git a/server/sim_hardware.c b/server/sim_hardware.c index 057a247..2450778 100644 --- a/server/sim_hardware.c +++ b/server/sim_hardware.c @@ -188,6 +188,13 @@ void hw_get_start_ts(struct timespec *ts) } +void hw_get_hw_start_ts(struct timespec *ts) +{ + ts->tv_sec = 0; + ts->tv_nsec = 0; +} + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Long table support. */