| @@ -64,6 +64,8 @@ | |||||
| #define PLSR_PROFILE_REFILL_BUDGET (200U) | #define PLSR_PROFILE_REFILL_BUDGET (200U) | ||||
| #define PLSR_PROFILE_LOW_WATER (100U) | #define PLSR_PROFILE_LOW_WATER (100U) | ||||
| #define PLSR_PROFILE_LOW_WATER_CLEAR (200U) | #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_HANDOFF_WARMUP_ITEMS (8U) | ||||
| #define PLSR_REPLAN_PREFIX_ITEMS (512U) | #define PLSR_REPLAN_PREFIX_ITEMS (512U) | ||||
| #define PLSR_REPLAN_PREFIX_TIME_US (5000ULL) | #define PLSR_REPLAN_PREFIX_TIME_US (5000ULL) | ||||
| @@ -130,8 +132,11 @@ typedef struct | |||||
| volatile uint32_t generation; | volatile uint32_t generation; | ||||
| uint32_t producerEpoch; | uint32_t producerEpoch; | ||||
| volatile uint32_t repeatRemaining; | volatile uint32_t repeatRemaining; | ||||
| volatile uint32_t underrunDebtPulses; | |||||
| PLSR_PLATFORM_TIMER_SETTING lastSetting; | |||||
| uint8_t producerSegment; | uint8_t producerSegment; | ||||
| uint8_t preparedHandoffBank; | uint8_t preparedHandoffBank; | ||||
| volatile uint8_t haveLastSetting; | |||||
| volatile uint8_t active; | volatile uint8_t active; | ||||
| volatile uint8_t generatorComplete; | volatile uint8_t generatorComplete; | ||||
| } PLSR_PROFILE_QUEUE; | } PLSR_PROFILE_QUEUE; | ||||
| @@ -357,10 +362,15 @@ static uint8_t PlsrPrepareShortProfile(PLSR_SHORT_PROFILE *profile, | |||||
| uint64_t pulseCount); | uint64_t pulseCount); | ||||
| static uint8_t PlsrShortProfileTakeRun(PLSR_SHORT_PROFILE *profile, | static uint8_t PlsrShortProfileTakeRun(PLSR_SHORT_PROFILE *profile, | ||||
| PLSR_PROFILE_ENTRY *entry); | PLSR_PROFILE_ENTRY *entry); | ||||
| static uint8_t PlsrShortProfileTakeRunLimited( | |||||
| PLSR_SHORT_PROFILE *profile, | |||||
| PLSR_PROFILE_ENTRY *entry, | |||||
| uint32_t maximumRepeats); | |||||
| static void PlsrProfileRecordPlannerStatus( | static void PlsrProfileRecordPlannerStatus( | ||||
| const PLSR_SHORT_PROFILE *profile); | 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 PlsrProfileQueueReset(void); | ||||
| static void PlsrProfileQueueInvalidateGeneration(void); | static void PlsrProfileQueueInvalidateGeneration(void); | ||||
| static uint8_t PlsrProfileQueueBegin( | static uint8_t PlsrProfileQueueBegin( | ||||
| @@ -370,6 +380,12 @@ static uint8_t PlsrProfileQueueBegin( | |||||
| uint8_t preparedHandoffBank); | uint8_t preparedHandoffBank); | ||||
| static uint8_t PlsrProfileQueueFill(uint16_t targetCount, | static uint8_t PlsrProfileQueueFill(uint16_t targetCount, | ||||
| uint16_t *itemBudget); | 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 uint8_t PlsrStageCountedHandoff(void); | ||||
| static PLSR_PLATFORM_QUEUE_RESULT PlsrProfileQueueCommitNext( | static PLSR_PLATFORM_QUEUE_RESULT PlsrProfileQueueCommitNext( | ||||
| uint8_t pulseOutput); | uint8_t pulseOutput); | ||||
| @@ -1770,11 +1786,19 @@ static uint8_t PlsrPrepareShortProfile(PLSR_SHORT_PROFILE *profile, | |||||
| static uint8_t PlsrShortProfileTakeRun(PLSR_SHORT_PROFILE *profile, | static uint8_t PlsrShortProfileTakeRun(PLSR_SHORT_PROFILE *profile, | ||||
| PLSR_PROFILE_ENTRY *entry) | 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; | uint32_t repeatCount; | ||||
| if ((profile->nextPeriod >= profile->pulseCount) | if ((profile->nextPeriod >= profile->pulseCount) | ||||
| || (entry == NULL)) | |||||
| || (entry == NULL) || (maximumRepeats == 0UL)) | |||||
| { | { | ||||
| return 0U; | return 0U; | ||||
| } | } | ||||
| @@ -1793,6 +1817,10 @@ static uint8_t PlsrShortProfileTakeRun(PLSR_SHORT_PROFILE *profile, | |||||
| { | { | ||||
| repeatCount = profile->pulseCount - profile->nextPeriod; | repeatCount = profile->pulseCount - profile->nextPeriod; | ||||
| } | } | ||||
| if (repeatCount > maximumRepeats) | |||||
| { | |||||
| repeatCount = maximumRepeats; | |||||
| } | |||||
| if (repeatCount == 0UL) | if (repeatCount == 0UL) | ||||
| { | { | ||||
| profile->active = 0U; | 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 | /* 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 | consuming the published bank. A bounded prefix from the old queue bridges | ||||
| the construction interval; the final bank flip is the only critical part. */ | 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 *source; | ||||
| PLSR_PROFILE_QUEUE *destination; | PLSR_PROFILE_QUEUE *destination; | ||||
| @@ -1835,6 +1864,7 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, | |||||
| uint32_t availablePlanPulses; | uint32_t availablePlanPulses; | ||||
| uint32_t replacementPulses; | uint32_t replacementPulses; | ||||
| uint32_t prefixCount = 0UL; | uint32_t prefixCount = 0UL; | ||||
| uint32_t replacementStartIndex; | |||||
| uint32_t consumedPrefix; | uint32_t consumedPrefix; | ||||
| uint32_t sourceIndex; | uint32_t sourceIndex; | ||||
| uint32_t criticalState; | uint32_t criticalState; | ||||
| @@ -1844,7 +1874,7 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, | |||||
| uint8_t sourceBank; | uint8_t sourceBank; | ||||
| uint8_t destinationBank; | uint8_t destinationBank; | ||||
| uint8_t boundaryWriteIndex; | uint8_t boundaryWriteIndex; | ||||
| uint8_t pipelineStartsNextSegment; | |||||
| uint8_t pipelineStartsNextSegment = 0U; | |||||
| uint8_t tailWasClipped = 0U; | uint8_t tailWasClipped = 0U; | ||||
| uint16_t fillBudget = PLSR_REPLAN_BUILD_ITEMS; | uint16_t fillBudget = PLSR_REPLAN_BUILD_ITEMS; | ||||
| @@ -1862,16 +1892,28 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, | |||||
| boundaryWriteIndex = PlsrCountedBoundaryWriteIndex; | boundaryWriteIndex = PlsrCountedBoundaryWriteIndex; | ||||
| if ((PlsrPulseActive == 0U) || (segmentNumber == 0U) | if ((PlsrPulseActive == 0U) || (segmentNumber == 0U) | ||||
| || (segmentNumber > PlsrActiveConfig.segmentCount) | || (segmentNumber > PlsrActiveConfig.segmentCount) | ||||
| || (boundaryWriteIndex != PlsrCountedBoundaryReadIndex) | |||||
| || (PlsrCountedFaultEvent != 0U) | |||||
| || (PlsrPlatformFinitePipelineSnapshot( | |||||
| (uint8_t)PlsrActiveConfig.pulseOutput, | |||||
| &committedPulses, &tailFrequencyHz, | |||||
| &pipelineStartsNextSegment) == 0U) | |||||
| || (pipelineStartsNextSegment != 0U)) | |||||
| || (PlsrCountedFaultEvent != 0U)) | |||||
| { | { | ||||
| PlsrPlatformExitCritical(criticalState); | 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); | PlsrPlatformExitCritical(criticalState); | ||||
| @@ -1884,6 +1926,8 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, | |||||
| destination->readIndex = 0UL; | destination->readIndex = 0UL; | ||||
| destination->writeIndex = 0UL; | destination->writeIndex = 0UL; | ||||
| destination->repeatRemaining = 0UL; | destination->repeatRemaining = 0UL; | ||||
| destination->underrunDebtPulses = 0UL; | |||||
| destination->haveLastSetting = 0U; | |||||
| destination->producerEpoch = epoch; | destination->producerEpoch = epoch; | ||||
| destination->producerSegment = segmentNumber; | destination->producerSegment = segmentNumber; | ||||
| destination->preparedHandoffBank = handoffBank; | destination->preparedHandoffBank = handoffBank; | ||||
| @@ -1912,7 +1956,7 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, | |||||
| frequencyHz = entry.setting.actualFrequencyHz; | frequencyHz = entry.setting.actualFrequencyHz; | ||||
| if (frequencyHz == 0UL) | if (frequencyHz == 0UL) | ||||
| { | { | ||||
| return 0U; | |||||
| return PLSR_PLATFORM_SERVICE_FAILED; | |||||
| } | } | ||||
| remainingPrefixTimeUs = | remainingPrefixTimeUs = | ||||
| PLSR_REPLAN_PREFIX_TIME_US - prefixTimeUs; | PLSR_REPLAN_PREFIX_TIME_US - prefixTimeUs; | ||||
| @@ -1943,6 +1987,7 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, | |||||
| } | } | ||||
| replacementPulses = availablePlanPulses - (uint32_t)prefixPulses; | replacementPulses = availablePlanPulses - (uint32_t)prefixPulses; | ||||
| replacementStartIndex = prefixCount; | |||||
| if (replacementPulses != 0UL) | if (replacementPulses != 0UL) | ||||
| { | { | ||||
| if ((tailFrequencyHz == 0UL) | if ((tailFrequencyHz == 0UL) | ||||
| @@ -1950,7 +1995,7 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, | |||||
| tailFrequencyHz, targetHz, | tailFrequencyHz, targetHz, | ||||
| replacementPulses) == 0U)) | replacementPulses) == 0U)) | ||||
| { | { | ||||
| return 0U; | |||||
| return PLSR_PLATFORM_SERVICE_FAILED; | |||||
| } | } | ||||
| PlsrCopyShortProfile(&destination->producerProfile, &replacement); | PlsrCopyShortProfile(&destination->producerProfile, &replacement); | ||||
| destination->active = 1U; | destination->active = 1U; | ||||
| @@ -1962,9 +2007,24 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, | |||||
| fillBudget--; | fillBudget--; | ||||
| if (PlsrShortProfileTakeRun(&candidate, &entry) == 0U) | 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); | PlsrCopyShortProfile(&destination->producerProfile, &candidate); | ||||
| destination->generatorComplete = | destination->generatorComplete = | ||||
| @@ -1996,7 +2056,7 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, | |||||
| && ((currentReadIndex - snapshotReadIndex) >= prefixCount))) | && ((currentReadIndex - snapshotReadIndex) >= prefixCount))) | ||||
| { | { | ||||
| PlsrPlatformExitCritical(criticalState); | PlsrPlatformExitCritical(criticalState); | ||||
| return 0U; | |||||
| return PLSR_PLATFORM_SERVICE_DEFERRED; | |||||
| } | } | ||||
| consumedPrefix = currentReadIndex - snapshotReadIndex; | consumedPrefix = currentReadIndex - snapshotReadIndex; | ||||
| destination->readIndex = consumedPrefix; | destination->readIndex = consumedPrefix; | ||||
| @@ -2018,7 +2078,7 @@ static uint8_t PlsrReplanPulseDir(uint32_t targetHz, | |||||
| PlsrProfileRecordPlannerStatus(&replacement); | PlsrProfileRecordPlannerStatus(&replacement); | ||||
| PlsrRefreshCurrentHandoffPlan(replacement.endHz); | PlsrRefreshCurrentHandoffPlan(replacement.endHz); | ||||
| return 1U; | |||||
| return PLSR_PLATFORM_SERVICE_READY; | |||||
| } | } | ||||
| static void PlsrCopyShortProfile(PLSR_SHORT_PROFILE *destination, | static void PlsrCopyShortProfile(PLSR_SHORT_PROFILE *destination, | ||||
| @@ -2099,8 +2159,10 @@ static void PlsrProfileQueueReset(void) | |||||
| PlsrProfileQueues[bank].generatorComplete = 0U; | PlsrProfileQueues[bank].generatorComplete = 0U; | ||||
| PlsrProfileQueues[bank].producerEpoch = 0UL; | PlsrProfileQueues[bank].producerEpoch = 0UL; | ||||
| PlsrProfileQueues[bank].repeatRemaining = 0UL; | PlsrProfileQueues[bank].repeatRemaining = 0UL; | ||||
| PlsrProfileQueues[bank].underrunDebtPulses = 0UL; | |||||
| PlsrProfileQueues[bank].producerSegment = 0U; | PlsrProfileQueues[bank].producerSegment = 0U; | ||||
| PlsrProfileQueues[bank].preparedHandoffBank = 0U; | PlsrProfileQueues[bank].preparedHandoffBank = 0U; | ||||
| PlsrProfileQueues[bank].haveLastSetting = 0U; | |||||
| PlsrProfileQueues[bank].producerProfile.active = 0U; | PlsrProfileQueues[bank].producerProfile.active = 0U; | ||||
| } | } | ||||
| PlsrProfileQueueBank = 0U; | PlsrProfileQueueBank = 0U; | ||||
| @@ -2126,6 +2188,8 @@ static uint8_t PlsrProfileQueueBegin( | |||||
| queue->preparedHandoffBank = preparedHandoffBank; | queue->preparedHandoffBank = preparedHandoffBank; | ||||
| queue->generatorComplete = 0U; | queue->generatorComplete = 0U; | ||||
| queue->repeatRemaining = 0UL; | queue->repeatRemaining = 0UL; | ||||
| queue->underrunDebtPulses = 0UL; | |||||
| queue->haveLastSetting = 0U; | |||||
| PlsrProfileQueueMinimumDepth = PLSR_PROFILE_QUEUE_CAPACITY; | PlsrProfileQueueMinimumDepth = PLSR_PROFILE_QUEUE_CAPACITY; | ||||
| PlsrProfileQueueLowWaterLatched = 0U; | PlsrProfileQueueLowWaterLatched = 0U; | ||||
| PlsrCopyShortProfile(&queue->producerProfile, producerProfile); | PlsrCopyShortProfile(&queue->producerProfile, producerProfile); | ||||
| @@ -2154,9 +2218,61 @@ static void PlsrProfileQueueInvalidateGeneration(void) | |||||
| queue->readIndex = 0U; | queue->readIndex = 0U; | ||||
| queue->writeIndex = 0U; | queue->writeIndex = 0U; | ||||
| queue->repeatRemaining = 0UL; | queue->repeatRemaining = 0UL; | ||||
| queue->underrunDebtPulses = 0UL; | |||||
| queue->haveLastSetting = 0U; | |||||
| PlsrPlatformExitCritical(criticalState); | 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) \ | #if defined(PLSR_DEBUG_TIMING) && (PLSR_DEBUG_TIMING != 0) \ | ||||
| && !defined(PLSR_HOST_TEST) | && !defined(PLSR_HOST_TEST) | ||||
| static uint32_t PlsrProfileTimingNow(void) | static uint32_t PlsrProfileTimingNow(void) | ||||
| @@ -2191,7 +2307,18 @@ static uint8_t PlsrProfileQueueFill(uint16_t targetCount, | |||||
| uint32_t generation; | uint32_t generation; | ||||
| uint32_t producerEpoch; | uint32_t producerEpoch; | ||||
| uint32_t criticalState; | 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) \ | #if defined(PLSR_DEBUG_TIMING) && (PLSR_DEBUG_TIMING != 0) \ | ||||
| && !defined(PLSR_HOST_TEST) | && !defined(PLSR_HOST_TEST) | ||||
| uint32_t startCycles; | uint32_t startCycles; | ||||
| @@ -2209,51 +2336,88 @@ static uint8_t PlsrProfileQueueFill(uint16_t targetCount, | |||||
| while (1) | while (1) | ||||
| { | { | ||||
| criticalState = PlsrPlatformEnterCritical(); | 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)) | || (*itemBudget == 0U)) | ||||
| { | { | ||||
| PlsrPlatformExitCritical(criticalState); | PlsrPlatformExitCritical(criticalState); | ||||
| return 1U; | return 1U; | ||||
| } | } | ||||
| (*itemBudget)--; | (*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); | PlsrPlatformExitCritical(criticalState); | ||||
| #if defined(PLSR_DEBUG_TIMING) && (PLSR_DEBUG_TIMING != 0) \ | #if defined(PLSR_DEBUG_TIMING) && (PLSR_DEBUG_TIMING != 0) \ | ||||
| && !defined(PLSR_HOST_TEST) | && !defined(PLSR_HOST_TEST) | ||||
| startCycles = PlsrProfileTimingNow(); | startCycles = PlsrProfileTimingNow(); | ||||
| #endif | #endif | ||||
| if (PlsrShortProfileTakeRun(&candidate, &entry) == 0U) | |||||
| if (PlsrShortProfileTakeRunLimited( | |||||
| &candidate, &entry, | |||||
| (payingUnderrunDebt != 0U) | |||||
| ? underrunDebt : 0xFFFFFFFFUL) == 0U) | |||||
| { | { | ||||
| return 0U; | return 0U; | ||||
| } | } | ||||
| criticalState = PlsrPlatformEnterCritical(); | 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); | PlsrPlatformExitCritical(criticalState); | ||||
| continue; | 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; | 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); | PlsrPlatformExitCritical(criticalState); | ||||
| #if defined(PLSR_DEBUG_TIMING) && (PLSR_DEBUG_TIMING != 0) \ | #if defined(PLSR_DEBUG_TIMING) && (PLSR_DEBUG_TIMING != 0) \ | ||||
| @@ -2559,6 +2723,8 @@ static uint8_t PlsrBeginSegmentOutput(uint32_t startFrequencyHz) | |||||
| } | } | ||||
| PlsrCurrentFrequencyHz = firstFrequencyHz; | PlsrCurrentFrequencyHz = firstFrequencyHz; | ||||
| PlsrQueuedFrequencyHz = secondFrequencyHz; | PlsrQueuedFrequencyHz = secondFrequencyHz; | ||||
| PlsrProfileQueue.lastSetting = firstRun.setting; | |||||
| PlsrProfileQueue.haveLastSetting = 1U; | |||||
| PlsrDiagnosticQueuedExpectedHz = firstRun.setting.actualFrequencyHz; | PlsrDiagnosticQueuedExpectedHz = firstRun.setting.actualFrequencyHz; | ||||
| PlsrCountedObservedPublished = PlsrPlatformObservedPulses( | PlsrCountedObservedPublished = PlsrPlatformObservedPulses( | ||||
| (uint8_t)PlsrActiveConfig.pulseOutput); | (uint8_t)PlsrActiveConfig.pulseOutput); | ||||
| @@ -3265,6 +3431,7 @@ uint8_t PlsrExecTakeCountedRunIrq( | |||||
| uint8_t *startsNextSegment) | uint8_t *startsNextSegment) | ||||
| { | { | ||||
| const PLSR_PROFILE_ENTRY *entry; | const PLSR_PROFILE_ENTRY *entry; | ||||
| PLSR_PROFILE_QUEUE *queue; | |||||
| uint32_t readIndex; | uint32_t readIndex; | ||||
| uint32_t nextReadIndex; | uint32_t nextReadIndex; | ||||
| @@ -3275,17 +3442,68 @@ uint8_t PlsrExecTakeCountedRunIrq( | |||||
| { | { | ||||
| return PLSR_EXEC_RUN_FAILED; | 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; | return PLSR_EXEC_RUN_DONE; | ||||
| } | } | ||||
| PlsrProfileQueueRecordUnderrun(); | 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) | if (entry->repeatCount == 0UL) | ||||
| { | { | ||||
| return PLSR_EXEC_RUN_FAILED; | return PLSR_EXEC_RUN_FAILED; | ||||
| @@ -3304,11 +3522,13 @@ uint8_t PlsrExecTakeCountedRunIrq( | |||||
| *pulseCount = entry->repeatCount; | *pulseCount = entry->repeatCount; | ||||
| *startsNextSegment = entry->startsNextSegment; | *startsNextSegment = entry->startsNextSegment; | ||||
| nextReadIndex = readIndex + 1UL; | 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( | PlsrProfileQueueRecordDepth( | ||||
| (uint16_t)(PlsrProfileQueue.writeIndex - nextReadIndex)); | |||||
| (uint16_t)(queue->writeIndex - nextReadIndex)); | |||||
| } | } | ||||
| /* 记录硬件即将生效的频率(事实镜像),不做任何状态转换。 */ | /* 记录硬件即将生效的频率(事实镜像),不做任何状态转换。 */ | ||||
| PlsrQueuedFrequencyHz = entry->setting.actualFrequencyHz; | PlsrQueuedFrequencyHz = entry->setting.actualFrequencyHz; | ||||
| @@ -3461,7 +3681,15 @@ static void PlsrExecServiceCountedBoundaryEvent(void) | |||||
| PlsrBoundaryPending = 0U; | PlsrBoundaryPending = 0U; | ||||
| PlsrBoundaryWasCut = 0U; | PlsrBoundaryWasCut = 0U; | ||||
| PlsrCutRequested = 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; | PlsrDeferredFrequencyPending = 0U; | ||||
| PlsrExtEdgePending = 0U; | PlsrExtEdgePending = 0U; | ||||
| PlsrExtPreviousLevel = | PlsrExtPreviousLevel = | ||||
| @@ -4206,7 +4434,8 @@ static uint8_t PlsrExecuteStop(void) | |||||
| PlsrRemainingPulses = drainPulses; | PlsrRemainingPulses = drainPulses; | ||||
| PlsrPlatformExitCritical(criticalState); | PlsrPlatformExitCritical(criticalState); | ||||
| if (PlsrReplanPulseDir(stopTargetHz, | if (PlsrReplanPulseDir(stopTargetHz, | ||||
| (uint32_t)drainPulses) == 0U) | |||||
| (uint32_t)drainPulses) | |||||
| != PLSR_PLATFORM_SERVICE_READY) | |||||
| { | { | ||||
| PlsrEnterError(PLSR_ERROR_TIMER); | PlsrEnterError(PLSR_ERROR_TIMER); | ||||
| return 1U; | return 1U; | ||||
| @@ -4401,6 +4630,8 @@ void PlsrPoll1ms(void) | |||||
| uint32_t pulseDirReplanPulses = 0UL; | uint32_t pulseDirReplanPulses = 0UL; | ||||
| uint32_t pollEpoch; | uint32_t pollEpoch; | ||||
| uint16_t fillBudget = PLSR_PROFILE_REFILL_BUDGET; | uint16_t fillBudget = PLSR_PROFILE_REFILL_BUDGET; | ||||
| PLSR_PLATFORM_SERVICE_RESULT pulseDirReplanResult = | |||||
| PLSR_PLATFORM_SERVICE_READY; | |||||
| if (PlsrInitialized == 0U) | if (PlsrInitialized == 0U) | ||||
| { | { | ||||
| @@ -4558,25 +4789,33 @@ void PlsrPoll1ms(void) | |||||
| && (PlsrAbStopArmed == 0U) | && (PlsrAbStopArmed == 0U) | ||||
| && (PlsrFrequencyUpdateSegment == activeSegmentNumber)) | && (PlsrFrequencyUpdateSegment == activeSegmentNumber)) | ||||
| { | { | ||||
| PlsrFrequencyUpdatePending = 0U; | |||||
| activeSegment->frequencyHz = newTargetHz; | activeSegment->frequencyHz = newTargetHz; | ||||
| PlsrBoundaryRampStarted = 0U; | PlsrBoundaryRampStarted = 0U; | ||||
| if (PlsrActiveConfig.outputMode == PLSR_OUTPUT_PULSE_DIR) | if (PlsrActiveConfig.outputMode == PLSR_OUTPUT_PULSE_DIR) | ||||
| { | { | ||||
| uint64_t remaining = PlsrRemainingPulses; | 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; | pulseDirReplanFailed = 1U; | ||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| PlsrFrequencyUpdatePending = 0U; | |||||
| pulseDirReplanPulses = (uint32_t)remaining; | pulseDirReplanPulses = (uint32_t)remaining; | ||||
| pulseDirReplanRequested = 1U; | pulseDirReplanRequested = 1U; | ||||
| } | } | ||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| PlsrFrequencyUpdatePending = 0U; | |||||
| PlsrShortProfile.active = 0U; | PlsrShortProfile.active = 0U; | ||||
| PlsrProfileQueueInvalidateGeneration(); | PlsrProfileQueueInvalidateGeneration(); | ||||
| PlsrRampStart(PlsrCurrentFrequencyHz, newTargetHz); | PlsrRampStart(PlsrCurrentFrequencyHz, newTargetHz); | ||||
| @@ -4588,11 +4827,29 @@ void PlsrPoll1ms(void) | |||||
| } | } | ||||
| PlsrPlatformExitCritical(criticalState); | 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) | if (pulseDirReplanFailed != 0U) | ||||
| @@ -5133,7 +5390,8 @@ PLSR_MB_RESULT PlsrModbusWriteHolding(uint16_t startAddress, | |||||
| criticalState = PlsrPlatformEnterCritical(); | criticalState = PlsrPlatformEnterCritical(); | ||||
| segmentBeforeDrain = PlsrCurrentSegment; | segmentBeforeDrain = PlsrCurrentSegment; | ||||
| if ((updateActiveFrequencies != 0U) && (PlsrPulseActive != 0U)) | |||||
| if ((updateActiveFrequencies != 0U) && (PlsrPulseActive != 0U) | |||||
| && (PlsrActiveConfig.outputMode != PLSR_OUTPUT_PULSE_DIR)) | |||||
| { | { | ||||
| PlsrPlatformDrainPendingPulse( | PlsrPlatformDrainPendingPulse( | ||||
| (uint8_t)PlsrActiveConfig.pulseOutput); | (uint8_t)PlsrActiveConfig.pulseOutput); | ||||
| @@ -5173,15 +5431,40 @@ PLSR_MB_RESULT PlsrModbusWriteHolding(uint16_t startAddress, | |||||
| } | } | ||||
| } | } | ||||
| PlsrPreparedHandoffBank = handoffBank; | 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) | if ((drainedToDifferentSegment != 0U) | ||||
| && (PlsrPulseActive != 0U) | && (PlsrPulseActive != 0U) | ||||
| @@ -5202,6 +5485,7 @@ PLSR_MB_RESULT PlsrModbusWriteHolding(uint16_t startAddress, | |||||
| if ((updateActiveFrequencies != 0U) | if ((updateActiveFrequencies != 0U) | ||||
| && (PlsrPulseActive != 0U) | && (PlsrPulseActive != 0U) | ||||
| && (PlsrActiveConfig.outputMode != PLSR_OUTPUT_PULSE_DIR) | |||||
| && (PlsrAbStopArmed == 0U) | && (PlsrAbStopArmed == 0U) | ||||
| && (PlsrFrequencyUpdatePending == 0U) | && (PlsrFrequencyUpdatePending == 0U) | ||||
| && (PlsrPrepareFutureHandoffQueue() == 0U)) | && (PlsrPrepareFutureHandoffQueue() == 0U)) | ||||