From 90dc11282850e3383ec23bf2e4d2a794f0dc1f2c Mon Sep 17 00:00:00 2001 From: ywh <2227158009@qq.com> Date: Sat, 15 Aug 2026 19:24:10 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E5=99=A8=E5=BC=82=E5=B8=B8=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PLSR/Src/plsr.c | 418 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 351 insertions(+), 67 deletions(-) diff --git a/PLSR/Src/plsr.c b/PLSR/Src/plsr.c index 5302966..638ab49 100644 --- a/PLSR/Src/plsr.c +++ b/PLSR/Src/plsr.c @@ -64,6 +64,8 @@ #define PLSR_PROFILE_REFILL_BUDGET (200U) #define PLSR_PROFILE_LOW_WATER (100U) #define PLSR_PROFILE_LOW_WATER_CLEAR (200U) +#define PLSR_UNDERRUN_HOLD_TIME_US (2000UL) +#define PLSR_UNDERRUN_HOLD_MAX_PULSES (200UL) #define PLSR_HANDOFF_WARMUP_ITEMS (8U) #define PLSR_REPLAN_PREFIX_ITEMS (512U) #define PLSR_REPLAN_PREFIX_TIME_US (5000ULL) @@ -130,8 +132,11 @@ typedef struct volatile uint32_t generation; uint32_t producerEpoch; volatile uint32_t repeatRemaining; + volatile uint32_t underrunDebtPulses; + PLSR_PLATFORM_TIMER_SETTING lastSetting; uint8_t producerSegment; uint8_t preparedHandoffBank; + volatile uint8_t haveLastSetting; volatile uint8_t active; volatile uint8_t generatorComplete; } PLSR_PROFILE_QUEUE; @@ -357,10 +362,15 @@ static uint8_t PlsrPrepareShortProfile(PLSR_SHORT_PROFILE *profile, uint64_t pulseCount); static uint8_t PlsrShortProfileTakeRun(PLSR_SHORT_PROFILE *profile, PLSR_PROFILE_ENTRY *entry); +static uint8_t PlsrShortProfileTakeRunLimited( + PLSR_SHORT_PROFILE *profile, + PLSR_PROFILE_ENTRY *entry, + uint32_t maximumRepeats); static void PlsrProfileRecordPlannerStatus( const PLSR_SHORT_PROFILE *profile); -static uint8_t PlsrReplanPulseDir(uint32_t targetHz, - uint32_t totalPulses); +static PLSR_PLATFORM_SERVICE_RESULT PlsrReplanPulseDir( + uint32_t targetHz, + uint32_t totalPulses); static void PlsrProfileQueueReset(void); static void PlsrProfileQueueInvalidateGeneration(void); static uint8_t PlsrProfileQueueBegin( @@ -370,6 +380,12 @@ static uint8_t PlsrProfileQueueBegin( uint8_t preparedHandoffBank); static uint8_t PlsrProfileQueueFill(uint16_t targetCount, uint16_t *itemBudget); +static uint8_t PlsrTimerSettingsEqual( + const PLSR_PLATFORM_TIMER_SETTING *first, + const PLSR_PLATFORM_TIMER_SETTING *second); +static uint8_t PlsrProfileQueueAppendLocked( + PLSR_PROFILE_QUEUE *queue, + const PLSR_PROFILE_ENTRY *entry); static uint8_t PlsrStageCountedHandoff(void); static PLSR_PLATFORM_QUEUE_RESULT PlsrProfileQueueCommitNext( uint8_t pulseOutput); @@ -1770,11 +1786,19 @@ static uint8_t PlsrPrepareShortProfile(PLSR_SHORT_PROFILE *profile, static uint8_t PlsrShortProfileTakeRun(PLSR_SHORT_PROFILE *profile, PLSR_PROFILE_ENTRY *entry) +{ + return PlsrShortProfileTakeRunLimited(profile, entry, 0xFFFFFFFFUL); +} + +static uint8_t PlsrShortProfileTakeRunLimited( + PLSR_SHORT_PROFILE *profile, + PLSR_PROFILE_ENTRY *entry, + uint32_t maximumRepeats) { uint32_t repeatCount; if ((profile->nextPeriod >= profile->pulseCount) - || (entry == NULL)) + || (entry == NULL) || (maximumRepeats == 0UL)) { return 0U; } @@ -1793,6 +1817,10 @@ static uint8_t PlsrShortProfileTakeRun(PLSR_SHORT_PROFILE *profile, { repeatCount = profile->pulseCount - profile->nextPeriod; } + if (repeatCount > maximumRepeats) + { + repeatCount = maximumRepeats; + } if (repeatCount == 0UL) { profile->active = 0U; @@ -1816,8 +1844,9 @@ static uint8_t PlsrShortProfileTakeRun(PLSR_SHORT_PROFILE *profile, /* Build the replacement stream in the inactive queue bank while the IRQ keeps consuming the published bank. A bounded prefix from the old queue bridges the construction interval; the final bank flip is the only critical part. */ -static uint8_t PlsrReplanPulseDir(uint32_t targetHz, - uint32_t totalPulses) +static PLSR_PLATFORM_SERVICE_RESULT PlsrReplanPulseDir( + uint32_t targetHz, + uint32_t totalPulses) { PLSR_PROFILE_QUEUE *source; PLSR_PROFILE_QUEUE *destination; @@ -1835,6 +1864,7 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, uint32_t availablePlanPulses; uint32_t replacementPulses; uint32_t prefixCount = 0UL; + uint32_t replacementStartIndex; uint32_t consumedPrefix; uint32_t sourceIndex; uint32_t criticalState; @@ -1844,7 +1874,7 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, uint8_t sourceBank; uint8_t destinationBank; uint8_t boundaryWriteIndex; - uint8_t pipelineStartsNextSegment; + uint8_t pipelineStartsNextSegment = 0U; uint8_t tailWasClipped = 0U; uint16_t fillBudget = PLSR_REPLAN_BUILD_ITEMS; @@ -1862,16 +1892,28 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, boundaryWriteIndex = PlsrCountedBoundaryWriteIndex; if ((PlsrPulseActive == 0U) || (segmentNumber == 0U) || (segmentNumber > PlsrActiveConfig.segmentCount) - || (boundaryWriteIndex != PlsrCountedBoundaryReadIndex) - || (PlsrCountedFaultEvent != 0U) - || (PlsrPlatformFinitePipelineSnapshot( - (uint8_t)PlsrActiveConfig.pulseOutput, - &committedPulses, &tailFrequencyHz, - &pipelineStartsNextSegment) == 0U) - || (pipelineStartsNextSegment != 0U)) + || (PlsrCountedFaultEvent != 0U)) { PlsrPlatformExitCritical(criticalState); - return 0U; + return PLSR_PLATFORM_SERVICE_FAILED; + } + if (boundaryWriteIndex != PlsrCountedBoundaryReadIndex) + { + PlsrPlatformExitCritical(criticalState); + return PLSR_PLATFORM_SERVICE_DEFERRED; + } + if (PlsrPlatformFinitePipelineSnapshot( + (uint8_t)PlsrActiveConfig.pulseOutput, + &committedPulses, &tailFrequencyHz, + &pipelineStartsNextSegment) == 0U) + { + PlsrPlatformExitCritical(criticalState); + return PLSR_PLATFORM_SERVICE_FAILED; + } + if (pipelineStartsNextSegment != 0U) + { + PlsrPlatformExitCritical(criticalState); + return PLSR_PLATFORM_SERVICE_DEFERRED; } PlsrPlatformExitCritical(criticalState); @@ -1884,6 +1926,8 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, destination->readIndex = 0UL; destination->writeIndex = 0UL; destination->repeatRemaining = 0UL; + destination->underrunDebtPulses = 0UL; + destination->haveLastSetting = 0U; destination->producerEpoch = epoch; destination->producerSegment = segmentNumber; destination->preparedHandoffBank = handoffBank; @@ -1912,7 +1956,7 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, frequencyHz = entry.setting.actualFrequencyHz; if (frequencyHz == 0UL) { - return 0U; + return PLSR_PLATFORM_SERVICE_FAILED; } remainingPrefixTimeUs = PLSR_REPLAN_PREFIX_TIME_US - prefixTimeUs; @@ -1943,6 +1987,7 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, } replacementPulses = availablePlanPulses - (uint32_t)prefixPulses; + replacementStartIndex = prefixCount; if (replacementPulses != 0UL) { if ((tailFrequencyHz == 0UL) @@ -1950,7 +1995,7 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, tailFrequencyHz, targetHz, replacementPulses) == 0U)) { - return 0U; + return PLSR_PLATFORM_SERVICE_FAILED; } PlsrCopyShortProfile(&destination->producerProfile, &replacement); destination->active = 1U; @@ -1962,9 +2007,24 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, fillBudget--; if (PlsrShortProfileTakeRun(&candidate, &entry) == 0U) { - return 0U; + return PLSR_PLATFORM_SERVICE_FAILED; + } + if ((prefixCount > replacementStartIndex) + && (destination->entries[prefixCount - 1UL] + .startsNextSegment == 0U) + && (PlsrTimerSettingsEqual( + &destination->entries[prefixCount - 1UL].setting, + &entry.setting) != 0U) + && (destination->entries[prefixCount - 1UL].repeatCount + <= (0xFFFFFFFFUL - entry.repeatCount))) + { + destination->entries[prefixCount - 1UL].repeatCount += + entry.repeatCount; + } + else + { + destination->entries[prefixCount++] = entry; } - destination->entries[prefixCount++] = entry; } PlsrCopyShortProfile(&destination->producerProfile, &candidate); destination->generatorComplete = @@ -1996,7 +2056,7 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, && ((currentReadIndex - snapshotReadIndex) >= prefixCount))) { PlsrPlatformExitCritical(criticalState); - return 0U; + return PLSR_PLATFORM_SERVICE_DEFERRED; } consumedPrefix = currentReadIndex - snapshotReadIndex; destination->readIndex = consumedPrefix; @@ -2018,7 +2078,7 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, PlsrProfileRecordPlannerStatus(&replacement); PlsrRefreshCurrentHandoffPlan(replacement.endHz); - return 1U; + return PLSR_PLATFORM_SERVICE_READY; } static void PlsrCopyShortProfile(PLSR_SHORT_PROFILE *destination, @@ -2099,8 +2159,10 @@ static void PlsrProfileQueueReset(void) PlsrProfileQueues[bank].generatorComplete = 0U; PlsrProfileQueues[bank].producerEpoch = 0UL; PlsrProfileQueues[bank].repeatRemaining = 0UL; + PlsrProfileQueues[bank].underrunDebtPulses = 0UL; PlsrProfileQueues[bank].producerSegment = 0U; PlsrProfileQueues[bank].preparedHandoffBank = 0U; + PlsrProfileQueues[bank].haveLastSetting = 0U; PlsrProfileQueues[bank].producerProfile.active = 0U; } PlsrProfileQueueBank = 0U; @@ -2126,6 +2188,8 @@ static uint8_t PlsrProfileQueueBegin( queue->preparedHandoffBank = preparedHandoffBank; queue->generatorComplete = 0U; queue->repeatRemaining = 0UL; + queue->underrunDebtPulses = 0UL; + queue->haveLastSetting = 0U; PlsrProfileQueueMinimumDepth = PLSR_PROFILE_QUEUE_CAPACITY; PlsrProfileQueueLowWaterLatched = 0U; PlsrCopyShortProfile(&queue->producerProfile, producerProfile); @@ -2154,9 +2218,61 @@ static void PlsrProfileQueueInvalidateGeneration(void) queue->readIndex = 0U; queue->writeIndex = 0U; queue->repeatRemaining = 0UL; + queue->underrunDebtPulses = 0UL; + queue->haveLastSetting = 0U; PlsrPlatformExitCritical(criticalState); } +/* Called with pulse IRQs masked. Quantized-equal adjacent settings form one + hardware run even if their requested ramp frequencies differ. Segment + markers remain separate because their completion owns the boundary event. */ +static uint8_t PlsrTimerSettingsEqual( + const PLSR_PLATFORM_TIMER_SETTING *first, + const PLSR_PLATFORM_TIMER_SETTING *second) +{ + return ((first->actualFrequencyHz == second->actualFrequencyHz) + && (first->prescaler == second->prescaler) + && (first->pairPrescaler == second->pairPrescaler) + && (first->period == second->period) + && (first->compare == second->compare)) ? 1U : 0U; +} + +static uint8_t PlsrProfileQueueAppendLocked( + PLSR_PROFILE_QUEUE *queue, + const PLSR_PROFILE_ENTRY *entry) +{ + PLSR_PROFILE_ENTRY *previous; + uint32_t writeIndex; + + if ((queue == NULL) || (entry == NULL) || (entry->repeatCount == 0UL)) + { + return 0U; + } + writeIndex = queue->writeIndex; + if (writeIndex != queue->readIndex) + { + previous = &queue->entries[ + (writeIndex - 1UL) & PLSR_PROFILE_QUEUE_MASK]; + if ((previous->startsNextSegment == 0U) + && (entry->startsNextSegment == 0U) + && (PlsrTimerSettingsEqual( + &previous->setting, &entry->setting) != 0U) + && (previous->repeatCount + <= (0xFFFFFFFFUL - entry->repeatCount))) + { + previous->repeatCount += entry->repeatCount; + return 1U; + } + } + if ((writeIndex - queue->readIndex) >= PLSR_PROFILE_QUEUE_CAPACITY) + { + return 0U; + } + queue->entries[writeIndex & PLSR_PROFILE_QUEUE_MASK] = *entry; + queue->writeIndex = writeIndex + 1UL; + return 1U; +} + #if defined(PLSR_DEBUG_TIMING) && (PLSR_DEBUG_TIMING != 0) \ && !defined(PLSR_HOST_TEST) static uint32_t PlsrProfileTimingNow(void) @@ -2191,7 +2307,18 @@ static uint8_t PlsrProfileQueueFill(uint16_t targetCount, uint32_t generation; uint32_t producerEpoch; uint32_t criticalState; - uint32_t writeIndex; + uint32_t underrunDebt; + uint32_t queueReadIndex; + uint32_t queueWriteIndex; + uint32_t queueCount; + uint32_t currentGeneration; + uint32_t currentProducerEpoch; + PLSR_PROFILE_QUEUE *queue; + uint8_t queueBank; + uint8_t currentBank; + uint8_t queueActive; + uint8_t generatorComplete; + uint8_t payingUnderrunDebt; #if defined(PLSR_DEBUG_TIMING) && (PLSR_DEBUG_TIMING != 0) \ && !defined(PLSR_HOST_TEST) uint32_t startCycles; @@ -2209,51 +2336,88 @@ static uint8_t PlsrProfileQueueFill(uint16_t targetCount, while (1) { criticalState = PlsrPlatformEnterCritical(); - if ((PlsrProfileQueue.active == 0U) - || (PlsrProfileQueue.generatorComplete != 0U) - || (PlsrProfileQueueCount() >= targetCount) + queueBank = PlsrProfileQueueBank; + queue = &PlsrProfileQueues[queueBank]; + queueActive = queue->active; + generatorComplete = queue->generatorComplete; + queueWriteIndex = queue->writeIndex; + queueReadIndex = queue->readIndex; + queueCount = queueWriteIndex - queueReadIndex; + if ((queueActive == 0U) + || (generatorComplete != 0U) + || (queueCount >= targetCount) || (*itemBudget == 0U)) { PlsrPlatformExitCritical(criticalState); return 1U; } (*itemBudget)--; - generation = PlsrProfileQueue.generation; - producerEpoch = PlsrProfileQueue.producerEpoch; - PlsrCopyShortProfile(&candidate, - &PlsrProfileQueue.producerProfile); + generation = queue->generation; + producerEpoch = queue->producerEpoch; + underrunDebt = queue->underrunDebtPulses; + payingUnderrunDebt = (underrunDebt != 0UL) ? 1U : 0U; + PlsrCopyShortProfile(&candidate, &queue->producerProfile); PlsrPlatformExitCritical(criticalState); #if defined(PLSR_DEBUG_TIMING) && (PLSR_DEBUG_TIMING != 0) \ && !defined(PLSR_HOST_TEST) startCycles = PlsrProfileTimingNow(); #endif - if (PlsrShortProfileTakeRun(&candidate, &entry) == 0U) + if (PlsrShortProfileTakeRunLimited( + &candidate, &entry, + (payingUnderrunDebt != 0U) + ? underrunDebt : 0xFFFFFFFFUL) == 0U) { return 0U; } criticalState = PlsrPlatformEnterCritical(); - if ((PlsrProfileQueue.active == 0U) - || (PlsrProfileQueue.generation != generation) - || (PlsrProfileQueue.producerEpoch != producerEpoch) - || (PlsrProfileQueueCount() >= PLSR_PROFILE_QUEUE_CAPACITY)) + currentBank = PlsrProfileQueueBank; + queueActive = queue->active; + currentGeneration = queue->generation; + currentProducerEpoch = queue->producerEpoch; + queueWriteIndex = queue->writeIndex; + queueReadIndex = queue->readIndex; + queueCount = queueWriteIndex - queueReadIndex; + if ((currentBank != queueBank) + || (queueActive == 0U) + || (currentGeneration != generation) + || (currentProducerEpoch != producerEpoch) + || (queueCount >= PLSR_PROFILE_QUEUE_CAPACITY)) { PlsrPlatformExitCritical(criticalState); continue; } - writeIndex = PlsrProfileQueue.writeIndex; - PlsrProfileQueue.entries[writeIndex & PLSR_PROFILE_QUEUE_MASK] = entry; - PlsrCopyShortProfile(&PlsrProfileQueue.producerProfile, &candidate); - PlsrProfileQueue.writeIndex = writeIndex + 1UL; - if (PlsrProfileQueueCount() >= PLSR_PROFILE_LOW_WATER_CLEAR) + PlsrCopyShortProfile(&queue->producerProfile, &candidate); + if (payingUnderrunDebt != 0U) + { + underrunDebt = queue->underrunDebtPulses; + if (underrunDebt < entry.repeatCount) + { + PlsrPlatformExitCritical(criticalState); + continue; + } + queue->underrunDebtPulses = + underrunDebt - entry.repeatCount; + } + else if (PlsrProfileQueueAppendLocked( + queue, &entry) == 0U) + { + PlsrPlatformExitCritical(criticalState); + continue; + } + queueWriteIndex = queue->writeIndex; + queueReadIndex = queue->readIndex; + queueCount = queueWriteIndex - queueReadIndex; + if (queueCount >= PLSR_PROFILE_LOW_WATER_CLEAR) { PlsrProfileQueueLowWaterLatched = 0U; } - if ((candidate.active == 0U) - || (candidate.nextPeriod >= candidate.pulseCount)) + if (((candidate.active == 0U) + || (candidate.nextPeriod >= candidate.pulseCount)) + && (queue->underrunDebtPulses == 0UL)) { - PlsrProfileQueue.generatorComplete = 1U; + queue->generatorComplete = 1U; } PlsrPlatformExitCritical(criticalState); #if defined(PLSR_DEBUG_TIMING) && (PLSR_DEBUG_TIMING != 0) \ @@ -2559,6 +2723,8 @@ static uint8_t PlsrBeginSegmentOutput(uint32_t startFrequencyHz) } PlsrCurrentFrequencyHz = firstFrequencyHz; PlsrQueuedFrequencyHz = secondFrequencyHz; + PlsrProfileQueue.lastSetting = firstRun.setting; + PlsrProfileQueue.haveLastSetting = 1U; PlsrDiagnosticQueuedExpectedHz = firstRun.setting.actualFrequencyHz; PlsrCountedObservedPublished = PlsrPlatformObservedPulses( (uint8_t)PlsrActiveConfig.pulseOutput); @@ -3265,6 +3431,7 @@ uint8_t PlsrExecTakeCountedRunIrq( uint8_t *startsNextSegment) { const PLSR_PROFILE_ENTRY *entry; + PLSR_PROFILE_QUEUE *queue; uint32_t readIndex; uint32_t nextReadIndex; @@ -3275,17 +3442,68 @@ uint8_t PlsrExecTakeCountedRunIrq( { return PLSR_EXEC_RUN_FAILED; } - readIndex = PlsrProfileQueue.readIndex; - if (readIndex == PlsrProfileQueue.writeIndex) + queue = &PlsrProfileQueues[PlsrProfileQueueBank]; + readIndex = queue->readIndex; + if (readIndex == queue->writeIndex) { - if (PlsrProfileQueue.generatorComplete != 0U) + uint32_t availablePulses; + uint32_t holdPulses; + uint32_t holdFrequencyHz; + + if (queue->generatorComplete != 0U) { return PLSR_EXEC_RUN_DONE; } PlsrProfileQueueRecordUnderrun(); - return PLSR_EXEC_RUN_FAILED; - } - entry = &PlsrProfileQueue.entries[readIndex & PLSR_PROFILE_QUEUE_MASK]; + /* A producer miss must not immediately turn the last scheduled edge + into an abrupt stop. Borrow at most 2 ms of still-unplanned logical + pulses and hold the last applied timer setting. The task repays + this debt by advancing (without enqueueing) the same number of + planner pulses, so segment pulse count remains exact. */ + availablePulses = + (queue->producerProfile.pulseCount + > queue->producerProfile.nextPeriod) + ? queue->producerProfile.pulseCount + - queue->producerProfile.nextPeriod + : 0UL; + if (availablePulses > queue->underrunDebtPulses) + { + availablePulses -= queue->underrunDebtPulses; + } + else + { + availablePulses = 0UL; + } + holdFrequencyHz = queue->lastSetting.actualFrequencyHz; + holdPulses = (uint32_t)( + ((uint64_t)holdFrequencyHz * PLSR_UNDERRUN_HOLD_TIME_US + + 999999ULL) / 1000000ULL); + if (holdPulses == 0UL) + { + holdPulses = 1UL; + } + if (holdPulses > PLSR_UNDERRUN_HOLD_MAX_PULSES) + { + holdPulses = PLSR_UNDERRUN_HOLD_MAX_PULSES; + } + if (holdPulses > availablePulses) + { + holdPulses = availablePulses; + } + if ((queue->active == 0U) || (queue->haveLastSetting == 0U) + || (holdFrequencyHz == 0UL) || (holdPulses == 0UL)) + { + return PLSR_EXEC_RUN_FAILED; + } + queue->underrunDebtPulses += holdPulses; + queue->generation++; + *setting = queue->lastSetting; + *pulseCount = holdPulses; + *startsNextSegment = 0U; + PlsrQueuedFrequencyHz = holdFrequencyHz; + return PLSR_EXEC_RUN_READY; + } + entry = &queue->entries[readIndex & PLSR_PROFILE_QUEUE_MASK]; if (entry->repeatCount == 0UL) { return PLSR_EXEC_RUN_FAILED; @@ -3304,11 +3522,13 @@ uint8_t PlsrExecTakeCountedRunIrq( *pulseCount = entry->repeatCount; *startsNextSegment = entry->startsNextSegment; nextReadIndex = readIndex + 1UL; - PlsrProfileQueue.readIndex = nextReadIndex; - if (PlsrProfileQueue.generatorComplete == 0U) + queue->readIndex = nextReadIndex; + queue->lastSetting = entry->setting; + queue->haveLastSetting = 1U; + if (queue->generatorComplete == 0U) { PlsrProfileQueueRecordDepth( - (uint16_t)(PlsrProfileQueue.writeIndex - nextReadIndex)); + (uint16_t)(queue->writeIndex - nextReadIndex)); } /* 记录硬件即将生效的频率(事实镜像),不做任何状态转换。 */ PlsrQueuedFrequencyHz = entry->setting.actualFrequencyHz; @@ -3461,7 +3681,15 @@ static void PlsrExecServiceCountedBoundaryEvent(void) PlsrBoundaryPending = 0U; PlsrBoundaryWasCut = 0U; PlsrCutRequested = 0U; - PlsrFrequencyUpdatePending = 0U; + if (PlsrFrequencyUpdatePending != 0U) + { + /* An online frequency transaction that met a latched segment marker + follows the hardware into the new segment instead of invalidating + the old handoff metadata. */ + PlsrFrequencyUpdateSegment = nextSegment; + PlsrFrequencyUpdateTargetHz = + PlsrActiveConfig.segments[nextSegment - 1U].frequencyHz; + } PlsrDeferredFrequencyPending = 0U; PlsrExtEdgePending = 0U; PlsrExtPreviousLevel = @@ -4206,7 +4434,8 @@ static uint8_t PlsrExecuteStop(void) PlsrRemainingPulses = drainPulses; PlsrPlatformExitCritical(criticalState); if (PlsrReplanPulseDir(stopTargetHz, - (uint32_t)drainPulses) == 0U) + (uint32_t)drainPulses) + != PLSR_PLATFORM_SERVICE_READY) { PlsrEnterError(PLSR_ERROR_TIMER); return 1U; @@ -4401,6 +4630,8 @@ void PlsrPoll1ms(void) uint32_t pulseDirReplanPulses = 0UL; uint32_t pollEpoch; uint16_t fillBudget = PLSR_PROFILE_REFILL_BUDGET; + PLSR_PLATFORM_SERVICE_RESULT pulseDirReplanResult = + PLSR_PLATFORM_SERVICE_READY; if (PlsrInitialized == 0U) { @@ -4558,25 +4789,33 @@ void PlsrPoll1ms(void) && (PlsrAbStopArmed == 0U) && (PlsrFrequencyUpdateSegment == activeSegmentNumber)) { - PlsrFrequencyUpdatePending = 0U; activeSegment->frequencyHz = newTargetHz; PlsrBoundaryRampStarted = 0U; if (PlsrActiveConfig.outputMode == PLSR_OUTPUT_PULSE_DIR) { uint64_t remaining = PlsrRemainingPulses; - if ((remaining == 0ULL) || (remaining > 0xFFFFFFFFULL)) + if (PlsrCountedHandoffStaged != 0U) { + /* The marker and its metadata are one published transaction. + Leave both untouched; the boundary consumer retargets this + pending update to the segment that hardware enters. */ + } + else if ((remaining == 0ULL) || (remaining > 0xFFFFFFFFULL)) + { + PlsrFrequencyUpdatePending = 0U; pulseDirReplanFailed = 1U; } else { + PlsrFrequencyUpdatePending = 0U; pulseDirReplanPulses = (uint32_t)remaining; pulseDirReplanRequested = 1U; } } else { + PlsrFrequencyUpdatePending = 0U; PlsrShortProfile.active = 0U; PlsrProfileQueueInvalidateGeneration(); PlsrRampStart(PlsrCurrentFrequencyHz, newTargetHz); @@ -4588,11 +4827,29 @@ void PlsrPoll1ms(void) } PlsrPlatformExitCritical(criticalState); - if ((pulseDirReplanRequested != 0U) - && (PlsrReplanPulseDir(newTargetHz, - pulseDirReplanPulses) == 0U)) + if (pulseDirReplanRequested != 0U) { - pulseDirReplanFailed = 1U; + pulseDirReplanResult = PlsrReplanPulseDir( + newTargetHz, pulseDirReplanPulses); + if (pulseDirReplanResult == PLSR_PLATFORM_SERVICE_FAILED) + { + pulseDirReplanFailed = 1U; + } + else if (pulseDirReplanResult == PLSR_PLATFORM_SERVICE_DEFERRED) + { + /* A segment-marker run may already be latched. Keep the old + published stream intact and retry against whichever segment is + active after the boundary event is settled. */ + criticalState = PlsrPlatformEnterCritical(); + if ((PlsrSegmentEpoch == pollEpoch) + && (PlsrStopRequested == 0U)) + { + PlsrFrequencyUpdateTargetHz = newTargetHz; + PlsrFrequencyUpdateSegment = activeSegmentNumber; + PlsrFrequencyUpdatePending = 1U; + } + PlsrPlatformExitCritical(criticalState); + } } if (pulseDirReplanFailed != 0U) @@ -5133,7 +5390,8 @@ PLSR_MB_RESULT PlsrModbusWriteHolding(uint16_t startAddress, criticalState = PlsrPlatformEnterCritical(); segmentBeforeDrain = PlsrCurrentSegment; - if ((updateActiveFrequencies != 0U) && (PlsrPulseActive != 0U)) + if ((updateActiveFrequencies != 0U) && (PlsrPulseActive != 0U) + && (PlsrActiveConfig.outputMode != PLSR_OUTPUT_PULSE_DIR)) { PlsrPlatformDrainPendingPulse( (uint8_t)PlsrActiveConfig.pulseOutput); @@ -5173,15 +5431,40 @@ PLSR_MB_RESULT PlsrModbusWriteHolding(uint16_t startAddress, } } PlsrPreparedHandoffBank = handoffBank; - PlsrHandoffPlan.valid = 0U; - PlsrProfileQueueInvalidateGeneration(); - if ((drainedToDifferentSegment == 0U) - && (PlsrPulseActive != 0U) - && (PlsrAbStopArmed == 0U) - && (PlsrShortProfile.active == 0U) - && (PlsrRemainingPulses == 1UL)) + if (PlsrActiveConfig.outputMode == PLSR_OUTPUT_PULSE_DIR) { - (void)PlsrPrimeHandoff(); + /* Publish the new prepared-plan bank without touching the stream + or handoff metadata already visible to the counter IRQ. A + task-context replan replaces the current tail atomically. If + the segment marker was already latched, the request remains + pending and is retargeted by the boundary event. */ + if ((PlsrPulseActive != 0U) + && (PlsrStopRequested == 0U) + && (PlsrAbStopArmed == 0U) + && (PlsrRunStatus != PLSR_STATUS_WAITING) + && (PlsrCurrentSegment != 0U) + && (PlsrCurrentSegment + <= PlsrActiveConfig.segmentCount)) + { + PlsrFrequencyUpdateSegment = PlsrCurrentSegment; + PlsrFrequencyUpdateTargetHz = + PlsrActiveConfig.segments[ + PlsrCurrentSegment - 1U].frequencyHz; + PlsrFrequencyUpdatePending = 1U; + } + } + else + { + PlsrHandoffPlan.valid = 0U; + PlsrProfileQueueInvalidateGeneration(); + if ((drainedToDifferentSegment == 0U) + && (PlsrPulseActive != 0U) + && (PlsrAbStopArmed == 0U) + && (PlsrShortProfile.active == 0U) + && (PlsrRemainingPulses == 1UL)) + { + (void)PlsrPrimeHandoff(); + } } if ((drainedToDifferentSegment != 0U) && (PlsrPulseActive != 0U) @@ -5202,6 +5485,7 @@ PLSR_MB_RESULT PlsrModbusWriteHolding(uint16_t startAddress, if ((updateActiveFrequencies != 0U) && (PlsrPulseActive != 0U) + && (PlsrActiveConfig.outputMode != PLSR_OUTPUT_PULSE_DIR) && (PlsrAbStopArmed == 0U) && (PlsrFrequencyUpdatePending == 0U) && (PlsrPrepareFutureHandoffQueue() == 0U))