Line data Source code
1 : /* 2 : * @file tooth_logger.cpp 3 : * 4 : * At least some of the code here is related to xxx.teeth files 5 : * See also misc\tooth_log_converter\log_convert.cpp 6 : * 7 : * @date Jul 7, 2019 8 : * @author Matthew Kennedy 9 : */ 10 : 11 : #include "pch.h" 12 : 13 : #if EFI_TOOTH_LOGGER 14 : #if !EFI_SHAFT_POSITION_INPUT 15 : fail("EFI_SHAFT_POSITION_INPUT required to have EFI_EMULATE_POSITION_SENSORS") 16 : #endif 17 : 18 : /** 19 : * Engine idles around 20Hz and revs up to 140Hz, at 60/2 and 8 cylinders we have about 20Khz events 20 : * If we can read buffer at 50Hz we want buffer to be about 400 elements. 21 : */ 22 : 23 : static_assert(sizeof(composite_logger_s) == COMPOSITE_PACKET_SIZE, "composite packet size"); 24 : 25 : static volatile bool ToothLoggerEnabled = false; 26 : //static uint32_t lastEdgeTimestamp = 0; 27 : 28 : static bool currentTrigger1 = false; 29 : static bool currentTrigger2 = false; 30 : static bool currentTdc = false; 31 : // any coil, all coils thrown together 32 : static bool currentCoilState = false; 33 : // same about injectors 34 : static bool currentInjectorState = false; 35 : 36 : #if EFI_UNIT_TEST 37 : #include "logicdata.h" 38 : 39 : static std::vector<CompositeEvent> events; 40 : 41 497 : const std::vector<CompositeEvent>& getCompositeEvents() { 42 497 : return events; 43 : } 44 : 45 70054 : void SetNextCompositeEntry(efitick_t timestamp) { 46 70054 : CompositeEvent event; 47 : 48 70054 : event.timestamp = timestamp; 49 70054 : event.primaryTrigger = currentTrigger1; 50 70054 : event.secondaryTrigger = currentTrigger2; 51 70054 : event.isTDC = currentTdc; 52 70054 : event.sync = engine->triggerCentral.triggerState.getShaftSynchronized(); 53 70054 : event.coil = currentCoilState; 54 70054 : event.injector = currentInjectorState; 55 : 56 70054 : events.push_back(event); 57 70054 : } 58 : 59 583 : void EnableToothLogger() { 60 583 : ToothLoggerEnabled = true; 61 583 : events.clear(); 62 583 : } 63 : 64 0 : void DisableToothLogger() { 65 0 : ToothLoggerEnabled = false; 66 0 : } 67 : 68 : #else // not EFI_UNIT_TEST 69 : 70 : static constexpr size_t BUFFER_COUNT = BIG_BUFFER_SIZE / sizeof(CompositeBuffer); 71 : static_assert(BUFFER_COUNT >= 2); 72 : 73 : static CompositeBuffer* buffers = nullptr; 74 : static chibios_rt::Mailbox<CompositeBuffer*, BUFFER_COUNT> freeBuffers CCM_OPTIONAL; 75 : static chibios_rt::Mailbox<CompositeBuffer*, BUFFER_COUNT> filledBuffers CCM_OPTIONAL; 76 : 77 : static CompositeBuffer* currentBuffer = nullptr; 78 : 79 : static void setToothLogReady(bool value) { 80 : #if EFI_TUNER_STUDIO && (EFI_PROD_CODE || EFI_SIMULATOR) 81 : engine->outputChannels.toothLogReady = value; 82 : #endif // EFI_TUNER_STUDIO 83 : } 84 : 85 : static BigBufferHandle bufferHandle; 86 : 87 : void EnableToothLogger() { 88 : chibios_rt::CriticalSectionLocker csl; 89 : 90 : bufferHandle = getBigBuffer(BigBufferUser::ToothLogger); 91 : if (!bufferHandle) { 92 : return; 93 : } 94 : 95 : buffers = bufferHandle.get<CompositeBuffer>(); 96 : 97 : // Reset all buffers 98 : for (size_t i = 0; i < BUFFER_COUNT; i++) { 99 : buffers[i].nextIdx = 0; 100 : } 101 : 102 : // Reset state 103 : currentBuffer = nullptr; 104 : 105 : // Empty the filled buffer list 106 : CompositeBuffer* dummy; 107 : while (MSG_TIMEOUT != filledBuffers.fetchI(&dummy)) ; 108 : 109 : // Put all buffers in the free list 110 : for (size_t i = 0; i < BUFFER_COUNT; i++) { 111 : freeBuffers.postI(&buffers[i]); 112 : } 113 : 114 : // Reset the last edge to now - this prevents the first edge logged from being bogus 115 : //lastEdgeTimestamp = getTimeNowUs(); 116 : 117 : // Enable logging of edges as they come 118 : ToothLoggerEnabled = true; 119 : 120 : setToothLogReady(false); 121 : } 122 : 123 : void DisableToothLogger() { 124 : chibios_rt::CriticalSectionLocker csl; 125 : 126 : ToothLoggerEnabled = false; 127 : setToothLogReady(false); 128 : 129 : // Release the big buffer for another user 130 : // C++ magic: here we are calling BigBufferHandle::operator=() with empty instance 131 : bufferHandle = {}; 132 : buffers = nullptr; 133 : } 134 : 135 : static CompositeBuffer* GetToothLoggerBufferImpl(sysinterval_t timeout) { 136 : CompositeBuffer* buffer; 137 : msg_t msg = filledBuffers.fetch(&buffer, timeout); 138 : 139 : if (msg == MSG_TIMEOUT) { 140 : setToothLogReady(false); 141 : return nullptr; 142 : } 143 : 144 : if (msg != MSG_OK) { 145 : // What even happened if we didn't get timeout, but also didn't get OK? 146 : return nullptr; 147 : } 148 : 149 : chibios_rt::CriticalSectionLocker csl; 150 : 151 : // If the used list is empty, clear the ready flag 152 : if (filledBuffers.getUsedCountI() == 0) { 153 : setToothLogReady(false); 154 : } 155 : 156 : return buffer; 157 : } 158 : 159 : CompositeBuffer* GetToothLoggerBufferNonblocking() { 160 : return GetToothLoggerBufferImpl(TIME_IMMEDIATE); 161 : } 162 : 163 : CompositeBuffer* GetToothLoggerBufferBlocking() { 164 : return GetToothLoggerBufferImpl(TIME_INFINITE); 165 : } 166 : 167 : void ReturnToothLoggerBuffer(CompositeBuffer* buffer) { 168 : chibios_rt::CriticalSectionLocker csl; 169 : 170 : msg_t msg = freeBuffers.postI(buffer); 171 : criticalAssertVoid(msg == MSG_OK, "Composite logger post to free buffer fail"); 172 : } 173 : 174 : static CompositeBuffer* findBuffer(efitick_t timestamp) { 175 : CompositeBuffer* buffer; 176 : 177 : if (!currentBuffer) { 178 : // try and find a buffer, if none available, we can't log 179 : if (MSG_OK != freeBuffers.fetchI(&buffer)) { 180 : return nullptr; 181 : } 182 : 183 : // Record the time of the last buffer swap so we can force a swap after a minimum period of time 184 : // This ensures the user sees *something* even if they don't have enough trigger events 185 : // to fill the buffer. 186 : buffer->startTime.reset(timestamp); 187 : buffer->nextIdx = 0; 188 : 189 : currentBuffer = buffer; 190 : } 191 : 192 : return currentBuffer; 193 : } 194 : 195 : static void SetNextCompositeEntry(efitick_t timestamp) { 196 : // This is called from multiple interrupts/threads, so we need a lock. 197 : chibios_rt::CriticalSectionLocker csl; 198 : 199 : CompositeBuffer* buffer = findBuffer(timestamp); 200 : 201 : if (!buffer) { 202 : // All buffers are full, nothing to do here. 203 : return; 204 : } 205 : 206 : size_t idx = buffer->nextIdx; 207 : auto nextIdx = idx + 1; 208 : buffer->nextIdx = nextIdx; 209 : 210 : if (idx < efi::size(buffer->buffer)) { 211 : composite_logger_s* entry = &buffer->buffer[idx]; 212 : 213 : uint32_t nowUs = NT2US(timestamp); 214 : 215 : // TS uses big endian, grumble 216 : entry->timestamp = SWAP_UINT32(nowUs); 217 : entry->priLevel = currentTrigger1; 218 : entry->secLevel = currentTrigger2; 219 : entry->trigger = currentTdc; 220 : entry->sync = engine->triggerCentral.triggerState.getShaftSynchronized(); 221 : entry->coil = currentCoilState; 222 : entry->injector = currentInjectorState; 223 : } 224 : 225 : // if the buffer is full... 226 : bool bufferFull = nextIdx >= efi::size(buffer->buffer); 227 : // ... or it's been too long since the last flush 228 : bool bufferTimedOut = buffer->startTime.hasElapsedSec(5); 229 : 230 : // Then cycle buffers and set the ready flag. 231 : if (bufferFull || bufferTimedOut) { 232 : // Post to the output queue 233 : filledBuffers.postI(buffer); 234 : 235 : // Null the current buffer so we get a new one next time 236 : currentBuffer = nullptr; 237 : 238 : // Flag that we are ready 239 : setToothLogReady(true); 240 : } 241 : } 242 : 243 : #endif // EFI_UNIT_TEST 244 : 245 67370 : void LogTriggerTooth(trigger_event_e tooth, efitick_t timestamp) { 246 : efiAssertVoid(ObdCode::CUSTOM_ERR_6650, hasLotsOfRemainingStack(), "l-t-t"); 247 : // bail if we aren't enabled 248 67370 : if (!ToothLoggerEnabled) { 249 0 : return; 250 : } 251 : 252 : // Don't log at significant engine speed 253 67370 : if (!getTriggerCentral()->isEngineSnifferEnabled) { 254 0 : return; 255 : } 256 : 257 67370 : ScopePerf perf(PE::LogTriggerTooth); 258 : 259 : /* 260 : // We currently only support the primary trigger falling edge 261 : // (this is the edge that VR sensors are accurate on) 262 : // Since VR sensors are the most useful case here, this is okay for now. 263 : if (tooth != SHAFT_PRIMARY_FALLING) { 264 : return; 265 : } 266 : 267 : uint32_t nowUs = NT2US(timestamp); 268 : // 10us per LSB - this gives plenty of accuracy, yet fits 655.35 ms in to a uint16 269 : uint16_t delta = static_cast<uint16_t>((nowUs - lastEdgeTimestamp) / 10); 270 : lastEdgeTimestamp = nowUs; 271 : 272 : SetNextEntry(delta); 273 : */ 274 : 275 67370 : switch (tooth) { 276 32354 : case SHAFT_PRIMARY_FALLING: 277 32354 : currentTrigger1 = false; 278 32354 : break; 279 32405 : case SHAFT_PRIMARY_RISING: 280 32405 : currentTrigger1 = true; 281 32405 : break; 282 1304 : case SHAFT_SECONDARY_FALLING: 283 1304 : currentTrigger2 = false; 284 1304 : break; 285 1307 : case SHAFT_SECONDARY_RISING: 286 1307 : currentTrigger2 = true; 287 1307 : break; 288 0 : default: 289 0 : break; 290 : } 291 : 292 67370 : SetNextCompositeEntry(timestamp); 293 : } 294 : 295 1342 : void LogTriggerTopDeadCenter(efitick_t timestamp) { 296 : // bail if we aren't enabled 297 1342 : if (!ToothLoggerEnabled) { 298 0 : return; 299 : } 300 1342 : currentTdc = true; 301 1342 : SetNextCompositeEntry(timestamp); 302 1342 : currentTdc = false; 303 1342 : SetNextCompositeEntry(timestamp + 10); 304 : } 305 : 306 10620 : void LogTriggerCoilState(efitick_t timestamp, bool state) { 307 10620 : if (!ToothLoggerEnabled) { 308 0 : return; 309 : } 310 10620 : currentCoilState = state; 311 : UNUSED(timestamp); 312 : //SetNextCompositeEntry(timestamp, trigger1, trigger2, trigger); 313 : } 314 : 315 5454 : void LogTriggerInjectorState(efitick_t timestamp, bool state) { 316 5454 : if (!ToothLoggerEnabled) { 317 0 : return; 318 : } 319 5454 : currentInjectorState = state; 320 : UNUSED(timestamp); 321 : //SetNextCompositeEntry(timestamp, trigger1, trigger2, trigger); 322 : } 323 : 324 0 : void EnableToothLoggerIfNotEnabled() { 325 0 : if (!ToothLoggerEnabled) { 326 0 : EnableToothLogger(); 327 : } 328 0 : } 329 : 330 0 : bool IsToothLoggerEnabled() { 331 0 : return ToothLoggerEnabled; 332 : } 333 : 334 : #endif /* EFI_TOOTH_LOGGER */