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 | 11 | size_t EVT4_state_size(void) { | |
| 16 | 11 | 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 | 11 | 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 | 11 | const uint32_t *restrict current = input_buffer->begin; | |
| 42 | 11 | 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 | 11 | size_t n_events_read = event_buffer->size; | |
| 48 | 11 | const size_t events_capacity = event_buffer->capacity; | |
| 49 | 11 | const size_t events_capacity_offset = events_capacity - 32; | |
| 50 | |||
| 51 | 11 | timestamp_t* restrict out_ts = event_buffer->t; | |
| 52 | 11 | uint16_t* restrict out_x = event_buffer->x; | |
| 53 | 11 | uint16_t* restrict out_y = event_buffer->y; | |
| 54 | 11 | uint8_t* restrict out_p = event_buffer->p; | |
| 55 | |||
| 56 | // Trigger output buffers | ||
| 57 | 11 | timestamp_t* restrict trigger_ts = trigger_buffer->t; | |
| 58 | 11 | uint8_t* restrict trigger_id = trigger_buffer->id; | |
| 59 | 11 | uint8_t* restrict trigger_p = trigger_buffer->p; | |
| 60 | |||
| 61 | 11 | size_t n_triggers_read = trigger_buffer->size; | |
| 62 | 11 | const size_t triggers_capacity = trigger_buffer->capacity; | |
| 63 | |||
| 64 | // State variables | ||
| 65 | 11 | uint64_t last_ts_high = state->last_ts_high; | |
| 66 | 11 | uint64_t ts_high_high = state->ts_high_high; | |
| 67 | |||
| 68 | 11 | parse_status_t status = EVUTILS_PARSE_OK; | |
| 69 | |||
| 70 | 11 | while( | |
| 71 | 4033 | current < end && | |
| 72 | 8077 | n_events_read < events_capacity_offset && | |
| 73 | n_triggers_read < triggers_capacity | ||
| 74 | ) { | ||
| 75 | |||
| 76 | 4033 | uint32_t packet_type = (*current & 0xF0000000) >> 28; | |
| 77 | 4033 | uint32_t packet_data = *current & 0x0FFFFFFF; | |
| 78 | |||
| 79 | 4033 | switch(packet_type){ | |
| 80 | 3222 | case EVT4_CD_OFF: | |
| 81 | case EVT4_CD_ON: | ||
| 82 | // Bits 27..22 low timestamp, 21..11 x, 10..0 y (same as EVT2). | ||
| 83 | 3222 | out_ts[n_events_read] = ts_high_high | last_ts_high | ((packet_data >> 22) & 0x3F); | |
| 84 | 3222 | out_x[n_events_read] = (packet_data >> 11) & 0x7FF; | |
| 85 | 3222 | out_y[n_events_read] = packet_data & 0x7FF; | |
| 86 | 3222 | out_p[n_events_read] = packet_type & 1; // CD_ON (0xB) -> 1 | |
| 87 | 3222 | n_events_read++; | |
| 88 | 3222 | break; | |
| 89 | 5 | case EVT4_CD_VEC_OFF: | |
| 90 | case EVT4_CD_VEC_ON: { | ||
| 91 | // Vector CD: this word is the base (x, y, low ts, polarity); the | ||
| 92 | // FOLLOWING word is a 32-bit validity mask. Each set bit `off` | ||
| 93 | // emits an event at x = base_x + off, same y/ts/polarity. | ||
| 94 | // If the mask word is not in this chunk, stop without consuming | ||
| 95 | // the base word so the caller resumes the whole group next time. | ||
| 96 | 5 | if (current + 1 >= end) { | |
| 97 | ✗ | status = EVUTILS_PARSE_INPUT_EMPTY; | |
| 98 | ✗ | goto done; | |
| 99 | } | ||
| 100 | 5 | const uint64_t ts = ts_high_high | last_ts_high | ((packet_data >> 22) & 0x3F); | |
| 101 | 5 | const uint16_t base_x = (packet_data >> 11) & 0x7FF; | |
| 102 | 5 | const uint16_t y = packet_data & 0x7FF; | |
| 103 | 5 | const uint8_t p = packet_type & 1; | |
| 104 | |||
| 105 | 5 | uint32_t mask = *(current + 1); | |
| 106 | 43 | while (mask) { | |
| 107 | 38 | uint32_t off = (uint32_t)__builtin_ctz(mask); | |
| 108 | 38 | out_ts[n_events_read] = ts; | |
| 109 | 38 | out_x[n_events_read] = (uint16_t)(base_x + off); | |
| 110 | 38 | out_y[n_events_read] = y; | |
| 111 | 38 | out_p[n_events_read] = p; | |
| 112 | 38 | n_events_read++; | |
| 113 | 38 | mask &= mask - 1u; | |
| 114 | } | ||
| 115 | 5 | current++; // consume the mask word (base word consumed below) | |
| 116 | 5 | break; | |
| 117 | } | ||
| 118 | 801 | case EVT4_EVT_TIME_HIGH: | |
| 119 | { | ||
| 120 | // Bits 27..0 are event-time bits 33..6. Track 28-bit wraps. | ||
| 121 | 801 | uint64_t new_ts_high = (uint64_t)packet_data << 6; | |
| 122 | 801 | if (new_ts_high < last_ts_high) { | |
| 123 | 2 | ts_high_high += EVT4_TS_WRAP; | |
| 124 | } | ||
| 125 | 801 | last_ts_high = new_ts_high; | |
| 126 | } | ||
| 127 | 801 | break; | |
| 128 | 5 | case EVT4_EXT_TRIGGER: | |
| 129 | // value @ bit 0, id @ bits 8..12 (5 bits), low ts @ bits 22..27. | ||
| 130 | 5 | trigger_ts[n_triggers_read] = ts_high_high | last_ts_high | ((packet_data >> 22) & 0x3F); | |
| 131 | 5 | trigger_id[n_triggers_read] = (packet_data >> 8) & 0x1F; | |
| 132 | 5 | trigger_p[n_triggers_read] = packet_data & 0x1; | |
| 133 | 5 | n_triggers_read++; | |
| 134 | 5 | break; | |
| 135 | ✗ | case EVT4_OTHERS: | |
| 136 | case EVT4_CONTINUED: | ||
| 137 | case EVT4_PADDING: | ||
| 138 | default: | ||
| 139 | ✗ | break; | |
| 140 | } | ||
| 141 | 4033 | current++; | |
| 142 | } | ||
| 143 | |||
| 144 | 11 | done: | |
| 145 | 11 | event_buffer->size = n_events_read; | |
| 146 | 11 | trigger_buffer->size = n_triggers_read; | |
| 147 | 11 | state->last_ts_high = last_ts_high; | |
| 148 | 11 | state->ts_high_high = ts_high_high; | |
| 149 | |||
| 150 | 11 | return (parser_result_t){ | |
| 151 | .current = (const void *)current, | ||
| 152 | .status = status | ||
| 153 | }; | ||
| 154 | } | ||
| 155 |