evt3.c
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | |||
| 2 | #include "evutils/evt3.h" | ||
| 3 | #include "evutils/types.h" | ||
| 4 | #include "evutils/parser.h" | ||
| 5 | |||
| 6 | #include <stdio.h> | ||
| 7 | #include <stdint.h> | ||
| 8 | |||
| 9 | #define EVT3_get_packet_type(packet) (((packet) >> 12) & 0xF) | ||
| 10 | #define EVT3_get_packet_data(packet) ((packet) & 0x0FFF) | ||
| 11 | |||
| 12 | enum EVT3_PacketType { | ||
| 13 | EVT3_EVT_ADDR_Y = 0x0, | ||
| 14 | EVT3_EVT_ADDR_X = 0x2, | ||
| 15 | EVT3_VECT_BASE_X = 0x3, | ||
| 16 | EVT3_VECT_12 = 0x4, | ||
| 17 | EVT3_VECT_8 = 0x5, | ||
| 18 | EVT3_EVT_TIME_LOW = 0x6, | ||
| 19 | EVT3_CONTINUED_4 = 0x7, | ||
| 20 | EVT3_EVT_TIME_HIGH = 0x8, | ||
| 21 | EVT3_EXT_TRIGGER = 0xA, | ||
| 22 | EVT3_OTHERS = 0xE, | ||
| 23 | EVT3_CONTINUED_12 = 0xF | ||
| 24 | }; | ||
| 25 | |||
| 26 | |||
| 27 | typedef struct evt3_state_s { | ||
| 28 | uint32_t ts_high_high; | ||
| 29 | uint32_t ts_high; | ||
| 30 | uint32_t ts_low; | ||
| 31 | uint32_t ts; | ||
| 32 | |||
| 33 | uint16_t y; | ||
| 34 | uint16_t vecbase_x; | ||
| 35 | uint8_t vecbase_p; | ||
| 36 | } evt3_state_t; | ||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | 84 | size_t EVT3_state_size(void) { | |
| 41 | 84 | return sizeof(evt3_state_t); | |
| 42 | } | ||
| 43 | |||
| 44 | |||
| 45 | #define EMIT_SOA() do { \ | ||
| 46 | unsigned has = (vec_valid != 0u); \ | ||
| 47 | uint32_t lz = (uint32_t)__builtin_ctz(vec_valid | 0x80000000u); \ | ||
| 48 | out_ts[n] = ts; \ | ||
| 49 | out_x[n] = (uint16_t)(bx + lz); \ | ||
| 50 | out_y[n] = y; \ | ||
| 51 | out_p[n] = p; \ | ||
| 52 | n += has; \ | ||
| 53 | vec_valid &= vec_valid - 1u; \ | ||
| 54 | } while (0) | ||
| 55 | |||
| 56 | |||
| 57 | __attribute__((always_inline)) | ||
| 58 | static inline const uint16_t * EVT3_parse_vector_12_12_8_soa( | ||
| 59 | const uint16_t * __restrict__ current, | ||
| 60 | evt3_state_t * __restrict__ state, | ||
| 61 | timestamp_t* __restrict__ out_ts, uint16_t* __restrict__ out_x, | ||
| 62 | uint16_t* __restrict__ out_y, uint8_t* __restrict__ out_p, | ||
| 63 | size_t * n_events) { | ||
| 64 | |||
| 65 | 46894109 | uint32_t vec_valid = EVT3_get_packet_data(*current); | |
| 66 | 46894109 | current++; | |
| 67 | |||
| 68 |
1/4✓ Branch 0 taken 46889725 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
46889725 | if(likely(EVT3_get_packet_type(*current) == EVT3_VECT_12)) { |
| 69 | 46894109 | vec_valid |= (uint32_t)(EVT3_get_packet_data(*current) << 12); | |
| 70 | 46894109 | current++; | |
| 71 | |||
| 72 |
2/8✓ Branch 0 taken 46889725 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4384 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
46894109 | if (likely(EVT3_get_packet_type(*current) == EVT3_VECT_8)) { |
| 73 | 46894109 | vec_valid |= (uint32_t)(*current & 0x00FF) << 24; | |
| 74 | 46894109 | current++; | |
| 75 | } | ||
| 76 | } | ||
| 77 | 46894109 | const uint32_t ts = state->ts; | |
| 78 | 46894109 | const uint16_t y = state->y; | |
| 79 | 46894109 | const uint8_t p = state->vecbase_p; | |
| 80 | 46894109 | const uint16_t bx = state->vecbase_x; | |
| 81 | 46894109 | size_t n = *n_events; | |
| 82 | |||
| 83 | 46894109 | EMIT_SOA(); EMIT_SOA(); EMIT_SOA(); EMIT_SOA(); /* 4 unconditional — no branch between them */ | |
| 84 | |||
| 85 |
4/8✓ Branch 0 taken 244738259 times.
✓ Branch 1 taken 46889725 times.
✓ Branch 2 taken 104900 times.
✓ Branch 3 taken 4384 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
291737268 | while (vec_valid) { EMIT_SOA(); } /* tail: only pop>4 (~23%) re-enters */ |
| 86 | |||
| 87 | 46894109 | *n_events = n; | |
| 88 | 46894109 | state->vecbase_x = bx + 32; | |
| 89 | |||
| 90 | 46894109 | return current; | |
| 91 | } | ||
| 92 | |||
| 93 | |||
| 94 | |||
| 95 | // TODO: For future performance optimization, implement dynamic CPU dispatch | ||
| 96 | // (e.g., using __attribute__((target_clones("avx2", "default"))) on GCC/Clang) | ||
| 97 | // or manual cpuid + function pointers to enable SIMD decoding on supported | ||
| 98 | // hardware without breaking universal portability on older CPUs. | ||
| 99 | |||
| 100 | |||
| 101 | #define EVT3_INPUT_PADDING 4 | ||
| 102 | EVUTILS_TARGET_CLONES | ||
| 103 | 728 | parser_result_t EVT3_parse_chunk_soa( | |
| 104 | evt3_state_t *state, | ||
| 105 | const evt3_input_buffer_t *input_buffer, | ||
| 106 | event_buffer_soa_t *event_buffer, | ||
| 107 | trigger_buffer_soa_t *trigger_buffer) { | ||
| 108 | |||
| 109 | // Hoist variables for better optimization | ||
| 110 | 728 | const uint16_t *restrict current = input_buffer->begin; | |
| 111 | 728 | const uint16_t * end_offset = input_buffer->end - EVT3_INPUT_PADDING; | |
| 112 | |||
| 113 | // Event output buffers | ||
| 114 | 728 | const size_t events_capacity = event_buffer->capacity; | |
| 115 | 728 | const size_t events_capacity_offset = events_capacity - 64; // We need to keep some space for vector messages | |
| 116 | 728 | size_t n_events_read = event_buffer->size; | |
| 117 | |||
| 118 | 728 | timestamp_t* restrict out_ts = event_buffer->t; | |
| 119 | 728 | uint16_t* restrict out_x = event_buffer->x; | |
| 120 | 728 | uint16_t* restrict out_y = event_buffer->y; | |
| 121 | 728 | uint8_t* restrict out_p = event_buffer->p; | |
| 122 | |||
| 123 | // Trigger output buffers | ||
| 124 | 728 | const size_t triggers_capacity = trigger_buffer->capacity; | |
| 125 | 728 | size_t n_triggers_read = trigger_buffer->size; | |
| 126 | |||
| 127 | 728 | timestamp_t* restrict trigger_ts = trigger_buffer->t; | |
| 128 | 728 | uint8_t* restrict trigger_id = trigger_buffer->id; | |
| 129 | 728 | uint8_t* restrict trigger_p = trigger_buffer->p; | |
| 130 | |||
| 131 | // State variables | ||
| 132 | 728 | evt3_state_t local_state = *state; | |
| 133 | |||
| 134 | 728 | parse_status_t status = EVUTILS_PARSE_OK; | |
| 135 | |||
| 136 | |||
| 137 | // Main parsing looop | ||
| 138 | 728 | while( | |
| 139 | 495310954 | current < end_offset && | |
| 140 | 495311075 | n_events_read < events_capacity_offset && | |
| 141 | n_triggers_read < triggers_capacity) { | ||
| 142 | |||
| 143 | 495310347 | uint32_t packet_type = EVT3_get_packet_type(*current); | |
| 144 | 495310347 | uint32_t packet_data = EVT3_get_packet_data(*current); | |
| 145 | |||
| 146 | // Most common case checks first | ||
| 147 | 495310347 | if(packet_type == EVT3_EVT_ADDR_X) { | |
| 148 | 267940330 | out_ts[n_events_read] = local_state.ts; | |
| 149 | 267940330 | out_y[n_events_read] = local_state.y; | |
| 150 | 267940330 | out_x[n_events_read] = (packet_data & 0x7FF); | |
| 151 | 267940330 | out_p[n_events_read] = (packet_data & 0x800) >> 11; | |
| 152 | 267940330 | n_events_read++; | |
| 153 | 267940330 | current++; | |
| 154 | 267940330 | continue; | |
| 155 | } | ||
| 156 | 227370017 | if(packet_type == EVT3_VECT_BASE_X) { | |
| 157 | // Implementation omitted for brevity | ||
| 158 | 31104420 | local_state.vecbase_x = packet_data & 0x7FF; | |
| 159 | 31104420 | local_state.vecbase_p = (packet_data & 0x800) >> 11; | |
| 160 | |||
| 161 | 31104420 | current++; | |
| 162 | |||
| 163 | // Parse vector messages | ||
| 164 | 77994145 | while (EVT3_get_packet_type(*current) == EVT3_VECT_12 && current < end_offset && n_events_read < events_capacity_offset) { | |
| 165 | 46889725 | current = EVT3_parse_vector_12_12_8_soa(current, &local_state, out_ts, out_x, out_y, out_p, &n_events_read); | |
| 166 | } | ||
| 167 | |||
| 168 | 31104420 | continue; | |
| 169 | } | ||
| 170 | |||
| 171 | 196265597 | switch(packet_type) { | |
| 172 | 148130243 | case EVT3_EVT_ADDR_Y: | |
| 173 | 148130243 | local_state.y = packet_data & 0x7FF; | |
| 174 | 148130243 | break; | |
| 175 | 8000681 | case EVT3_EVT_TIME_HIGH: | |
| 176 | { | ||
| 177 | 8000681 | uint32_t new_ts_high = packet_data << 12; | |
| 178 | |||
| 179 | 8000681 | if(unlikely(new_ts_high < local_state.ts_high)) { | |
| 180 | 222 | local_state.ts_high_high += 0x1000000; | |
| 181 | } | ||
| 182 | 8000681 | local_state.ts_high = new_ts_high; | |
| 183 | |||
| 184 | 8000681 | current++; | |
| 185 | 8000681 | packet_type = EVT3_get_packet_type(*current); | |
| 186 | |||
| 187 | 8000681 | if(unlikely(packet_type != EVT3_EVT_TIME_LOW)) { | |
| 188 | 3043089 | continue; | |
| 189 | } | ||
| 190 | 4957592 | packet_data = EVT3_get_packet_data(*current); | |
| 191 | } | ||
| 192 | __attribute__((fallthrough)); | ||
| 193 | 42040932 | case EVT3_EVT_TIME_LOW: | |
| 194 | 42040932 | local_state.ts_low = packet_data; | |
| 195 | 42040932 | local_state.ts = local_state.ts_high_high | local_state.ts_high | local_state.ts_low; | |
| 196 | 42040932 | break; | |
| 197 | 175 | case EVT3_EXT_TRIGGER: | |
| 198 | 175 | trigger_ts[n_triggers_read] = local_state.ts; | |
| 199 | 175 | trigger_id[n_triggers_read] = packet_data >> 8; | |
| 200 | 175 | trigger_p[n_triggers_read] = packet_data & 0x1; | |
| 201 | 175 | n_triggers_read++; | |
| 202 | 175 | break; | |
| 203 | 4384 | case EVT3_VECT_12: | |
| 204 | 4384 | current = EVT3_parse_vector_12_12_8_soa(current, &local_state, out_ts, out_x, out_y, out_p, &n_events_read); | |
| 205 | 4384 | continue; | |
| 206 | break; | ||
| 207 | case EVT3_VECT_8: | ||
| 208 | case EVT3_EVT_ADDR_X: | ||
| 209 | case EVT3_VECT_BASE_X: | ||
| 210 | // printf("Unexpected packet type %d in vector parsing\n", packet_type); | ||
| 211 | __builtin_unreachable(); | ||
| 212 | break; | ||
| 213 | 3046774 | case EVT3_OTHERS: | |
| 214 | case EVT3_CONTINUED_12: | ||
| 215 | case EVT3_CONTINUED_4: | ||
| 216 | default: | ||
| 217 | 3046774 | break; | |
| 218 | } | ||
| 219 | 193218124 | current++; | |
| 220 | } | ||
| 221 | |||
| 222 | 728 | *state = local_state; | |
| 223 | |||
| 224 | 728 | event_buffer->size = n_events_read; | |
| 225 | 728 | trigger_buffer->size = n_triggers_read; | |
| 226 | |||
| 227 | |||
| 228 | 728 | return (parser_result_t){ | |
| 229 | .current = (const void *)current, | ||
| 230 | .status = status | ||
| 231 | }; | ||
| 232 | } | ||
| 233 | |||
| 234 |