evt4.c
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "evutils/evt4.h" | ||
| 2 | |||
| 3 | /* EVT4 timestamp reconstruction is identical to EVT2: the 28-bit TIME_HIGH | ||
| 4 | * field carries time-base bits 6..33 (so it is shifted left by 6), and the CD / | ||
| 5 | * trigger words carry the low 6 bits. The field wraps at 2^34; bump the overflow | ||
| 6 | * accumulator each time it does. */ | ||
| 7 | typedef struct evt4_state_s { | ||
| 8 | uint64_t last_ts_high; /* current TIME_HIGH << 6 (bits 6..33) */ | ||
| 9 | uint64_t ts_high_high; /* accumulated overflow of the 28-bit field (34+) */ | ||
| 10 | } evt4_state_t; | ||
| 11 | |||
| 12 | #define EVT4_TS_WRAP (1ULL << 34) | ||
| 13 | |||
| 14 | |||
| 15 | 36 | size_t EVT4_state_size(void) { | |
| 16 | 36 | return sizeof(evt4_state_t); | |
| 17 | } | ||
| 18 | |||
| 19 | |||
| 20 | /* 4-bit type field in bits 28..31 of each 32-bit word. */ | ||
| 21 | enum EVT4_PacketType { | ||
| 22 | EVT4_OTHERS = 0x6, | ||
| 23 | EVT4_CONTINUED = 0x7, | ||
| 24 | EVT4_EXT_TRIGGER = 0x9, | ||
| 25 | EVT4_CD_OFF = 0xA, /* single CD event, polarity 0 */ | ||
| 26 | EVT4_CD_ON = 0xB, /* single CD event, polarity 1 */ | ||
| 27 | EVT4_CD_VEC_OFF = 0xC, /* vector CD base, polarity 0; next word = mask */ | ||
| 28 | EVT4_CD_VEC_ON = 0xD, /* vector CD base, polarity 1; next word = mask */ | ||
| 29 | EVT4_EVT_TIME_HIGH = 0xE, | ||
| 30 | EVT4_PADDING = 0xF | ||
| 31 | }; | ||
| 32 | |||
| 33 | |||
| 34 | EVUTILS_TARGET_CLONES | ||
| 35 | 38 | parser_result_t EVT4_parse_chunk_soa( | |
| 36 | evt4_state_t *state, | ||
| 37 | const evt4_input_buffer_t *input_buffer, | ||
| 38 | event_buffer_soa_t *event_buffer, | ||
| 39 | trigger_buffer_soa_t *trigger_buffer) { | ||
| 40 | |||
| 41 | 38 | const uint32_t *restrict current = input_buffer->begin; | |
| 42 | 38 | const uint32_t *restrict end = input_buffer->end; | |
| 43 | |||
| 44 | // Event output buffers. A single vector word expands to at most 32 events | ||
| 45 | // (one per set bit of the 32-bit mask), so stop early enough to always have | ||
| 46 | // room for a full expansion without a per-bit capacity check. | ||
| 47 | 38 | size_t n_events_read = event_buffer->size; | |
| 48 | 38 | const size_t events_capacity = event_buffer->capacity; | |
| 49 | // Guard the size_t subtraction: capacity <= 32 would wrap to a huge offset. | ||
| 50 | 38 | const size_t events_capacity_offset = events_capacity > 32 ? events_capacity - 32 : 0; | |
| 51 | |||
| 52 | 38 | timestamp_t* restrict out_ts = event_buffer->t; | |
| 53 | 38 | uint16_t* restrict out_x = event_buffer->x; | |
| 54 | 38 | uint16_t* restrict out_y = event_buffer->y; | |
| 55 | 38 | uint8_t* restrict out_p = event_buffer->p; | |
| 56 | |||
| 57 | // Trigger output buffers | ||
| 58 | 38 | timestamp_t* restrict trigger_ts = trigger_buffer->t; | |
| 59 | 38 | uint8_t* restrict trigger_id = trigger_buffer->id; | |
| 60 | 38 | uint8_t* restrict trigger_p = trigger_buffer->p; | |
| 61 | |||
| 62 | 38 | size_t n_triggers_read = trigger_buffer->size; | |
| 63 | 38 | const size_t triggers_capacity = trigger_buffer->capacity; | |
| 64 | |||
| 65 | // State variables | ||
| 66 | 38 | uint64_t last_ts_high = state->last_ts_high; | |
| 67 | 38 | uint64_t ts_high_high = state->ts_high_high; | |
| 68 | |||
| 69 | 38 | parse_status_t status = EVUTILS_PARSE_OK; | |
| 70 | |||
| 71 | 38 | while( | |
| 72 | 1035177 | current < end && | |
| 73 | 2070392 | n_events_read < events_capacity_offset && | |
| 74 | n_triggers_read < triggers_capacity | ||
| 75 | ) { | ||
| 76 | |||
| 77 | 1035177 | uint32_t packet_type = (*current & 0xF0000000) >> 28; | |
| 78 | 1035177 | uint32_t packet_data = *current & 0x0FFFFFFF; | |
| 79 | |||
| 80 | 1035177 | switch(packet_type){ | |
| 81 | 511042 | case EVT4_CD_OFF: | |
| 82 | case EVT4_CD_ON: | ||
| 83 | // Bits 27..22 low timestamp, 21..11 x, 10..0 y (same as EVT2). | ||
| 84 | 511042 | out_ts[n_events_read] = ts_high_high | last_ts_high | ((packet_data >> 22) & 0x3F); | |
| 85 | 511042 | out_x[n_events_read] = (packet_data >> 11) & 0x7FF; | |
| 86 | 511042 | out_y[n_events_read] = packet_data & 0x7FF; | |
| 87 | 511042 | out_p[n_events_read] = packet_type & 1; // CD_ON (0xB) -> 1 | |
| 88 | 511042 | n_events_read++; | |
| 89 | 511042 | break; | |
| 90 | 2787 | case EVT4_CD_VEC_OFF: | |
| 91 | case EVT4_CD_VEC_ON: { | ||
| 92 | // Vector CD: this word is the base (x, y, low ts, polarity); the | ||
| 93 | // FOLLOWING word is a 32-bit validity mask. Each set bit `off` | ||
| 94 | // emits an event at x = base_x + off, same y/ts/polarity. | ||
| 95 | // If the mask word is not in this chunk, stop without consuming | ||
| 96 | // the base word so the caller resumes the whole group next time. | ||
| 97 | 2787 | if (current + 1 >= end) { | |
| 98 | ✗ | status = EVUTILS_PARSE_INPUT_EMPTY; | |
| 99 | ✗ | goto done; | |
| 100 | } | ||
| 101 | 2787 | const uint64_t ts = ts_high_high | last_ts_high | ((packet_data >> 22) & 0x3F); | |
| 102 | 2787 | const uint16_t base_x = (packet_data >> 11) & 0x7FF; | |
| 103 | 2787 | const uint16_t y = packet_data & 0x7FF; | |
| 104 | 2787 | const uint8_t p = packet_type & 1; | |
| 105 | |||
| 106 | 2787 | uint32_t mask = *(current + 1); | |
| 107 | 47249 | while (mask) { | |
| 108 | 44462 | uint32_t off = (uint32_t)__builtin_ctz(mask); | |
| 109 | 44462 | out_ts[n_events_read] = ts; | |
| 110 | 44462 | out_x[n_events_read] = (uint16_t)(base_x + off); | |
| 111 | 44462 | out_y[n_events_read] = y; | |
| 112 | 44462 | out_p[n_events_read] = p; | |
| 113 | 44462 | n_events_read++; | |
| 114 | 44462 | mask &= mask - 1u; | |
| 115 | } | ||
| 116 | 2787 | current++; // consume the mask word (base word consumed below) | |
| 117 | 2787 | break; | |
| 118 | } | ||
| 119 | 506106 | case EVT4_EVT_TIME_HIGH: | |
| 120 | { | ||
| 121 | // Bits 27..0 are event-time bits 33..6. Track 28-bit wraps. | ||
| 122 | 506106 | uint64_t new_ts_high = (uint64_t)packet_data << 6; | |
| 123 | 506106 | if (new_ts_high < last_ts_high) { | |
| 124 | 707 | ts_high_high += EVT4_TS_WRAP; | |
| 125 | } | ||
| 126 | 506106 | last_ts_high = new_ts_high; | |
| 127 | } | ||
| 128 | 506106 | break; | |
| 129 | 1456 | case EVT4_EXT_TRIGGER: | |
| 130 | // value @ bit 0, id @ bits 8..12 (5 bits), low ts @ bits 22..27. | ||
| 131 | 1456 | trigger_ts[n_triggers_read] = ts_high_high | last_ts_high | ((packet_data >> 22) & 0x3F); | |
| 132 | 1456 | trigger_id[n_triggers_read] = (packet_data >> 8) & 0x1F; | |
| 133 | 1456 | trigger_p[n_triggers_read] = packet_data & 0x1; | |
| 134 | 1456 | n_triggers_read++; | |
| 135 | 1456 | break; | |
| 136 | 13786 | case EVT4_OTHERS: | |
| 137 | case EVT4_CONTINUED: | ||
| 138 | case EVT4_PADDING: | ||
| 139 | default: | ||
| 140 | 13786 | break; | |
| 141 | } | ||
| 142 | 1035177 | current++; | |
| 143 | } | ||
| 144 | |||
| 145 | 38 | done: | |
| 146 | 38 | event_buffer->size = n_events_read; | |
| 147 | 38 | trigger_buffer->size = n_triggers_read; | |
| 148 | 38 | state->last_ts_high = last_ts_high; | |
| 149 | 38 | state->ts_high_high = ts_high_high; | |
| 150 | |||
| 151 | /* Report why parsing stopped: output space exhausted vs input drained. */ | ||
| 152 | 38 | if (n_events_read >= events_capacity_offset || n_triggers_read >= triggers_capacity) { | |
| 153 | ✗ | status = EVUTILS_PARSE_OUTPUT_FULL; | |
| 154 | } | ||
| 155 | |||
| 156 | 38 | return (parser_result_t){ | |
| 157 | .current = (const void *)current, | ||
| 158 | .status = status | ||
| 159 | }; | ||
| 160 | } | ||
| 161 |