|
|
@@ -56,13 +56,12 @@ |
|
|
|| (PLSR_PROFILE_QUEUE_CAPACITY > 65535U)) |
|
|
|| (PLSR_PROFILE_QUEUE_CAPACITY > 65535U)) |
|
|
#error "PLSR profile queue capacity must be a power of two not exceeding 65535" |
|
|
#error "PLSR profile queue capacity must be a power of two not exceeding 65535" |
|
|
#endif |
|
|
#endif |
|
|
/* A profile item can represent only one pulse. At the 100 kHz limit these |
|
|
|
|
|
thresholds provide 4 ms of startup data, refill toward 8 ms, and cap each |
|
|
|
|
|
1 ms producer pass at 2 ms worth of newly planned items. */ |
|
|
|
|
|
|
|
|
/* At the 100 kHz limit these thresholds prefill and refill toward 8 ms of |
|
|
|
|
|
queued profile data. The caller bounds each producer service pass. */ |
|
|
#define PLSR_PROFILE_STARTUP_TARGET (800U) |
|
|
#define PLSR_PROFILE_STARTUP_TARGET (800U) |
|
|
#define PLSR_PROFILE_STARTUP_BUDGET (800U) |
|
|
#define PLSR_PROFILE_STARTUP_BUDGET (800U) |
|
|
#define PLSR_PROFILE_REFILL_TARGET (800U) |
|
|
#define PLSR_PROFILE_REFILL_TARGET (800U) |
|
|
#define PLSR_PROFILE_REFILL_BUDGET (200U) |
|
|
|
|
|
|
|
|
#define PLSR_PROFILE_PRODUCER_BATCH_ITEMS (16U) |
|
|
#define PLSR_TIMED_START_BUILD_BUDGET (32U) |
|
|
#define PLSR_TIMED_START_BUILD_BUDGET (32U) |
|
|
#define PLSR_TIMED_START_READY_ITEMS PLSR_PROFILE_STARTUP_BUDGET |
|
|
#define PLSR_TIMED_START_READY_ITEMS PLSR_PROFILE_STARTUP_BUDGET |
|
|
#define PLSR_PROFILE_LOW_WATER (100U) |
|
|
#define PLSR_PROFILE_LOW_WATER (100U) |
|
|
@@ -473,6 +472,9 @@ static uint8_t PlsrProfileQueueFill(uint16_t targetCount, |
|
|
static uint8_t PlsrTimerSettingsEqual( |
|
|
static uint8_t PlsrTimerSettingsEqual( |
|
|
const PLSR_PLATFORM_TIMER_SETTING *first, |
|
|
const PLSR_PLATFORM_TIMER_SETTING *first, |
|
|
const PLSR_PLATFORM_TIMER_SETTING *second); |
|
|
const PLSR_PLATFORM_TIMER_SETTING *second); |
|
|
|
|
|
static uint8_t PlsrProfileEntryTryMerge( |
|
|
|
|
|
PLSR_PROFILE_ENTRY *previous, |
|
|
|
|
|
const PLSR_PROFILE_ENTRY *entry); |
|
|
static uint8_t PlsrProfileQueueAppendLocked( |
|
|
static uint8_t PlsrProfileQueueAppendLocked( |
|
|
PLSR_PROFILE_QUEUE *queue, |
|
|
PLSR_PROFILE_QUEUE *queue, |
|
|
const PLSR_PROFILE_ENTRY *entry); |
|
|
const PLSR_PROFILE_ENTRY *entry); |
|
|
@@ -2663,6 +2665,33 @@ static uint8_t PlsrTimerSettingsEqual( |
|
|
&& (first->compare == second->compare)) ? 1U : 0U; |
|
|
&& (first->compare == second->compare)) ? 1U : 0U; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static uint8_t PlsrProfileEntryTryMerge( |
|
|
|
|
|
PLSR_PROFILE_ENTRY *previous, |
|
|
|
|
|
const PLSR_PROFILE_ENTRY *entry) |
|
|
|
|
|
{ |
|
|
|
|
|
uint32_t runLimit; |
|
|
|
|
|
|
|
|
|
|
|
if ((previous == NULL) || (entry == NULL) |
|
|
|
|
|
|| (previous->startsNextSegment != 0U) |
|
|
|
|
|
|| (entry->startsNextSegment != 0U) |
|
|
|
|
|
|| (PlsrTimerSettingsEqual(&previous->setting, |
|
|
|
|
|
&entry->setting) == 0U)) |
|
|
|
|
|
{ |
|
|
|
|
|
return 0U; |
|
|
|
|
|
} |
|
|
|
|
|
runLimit = PlsrProfileRunLimit( |
|
|
|
|
|
previous->setting.actualFrequencyHz); |
|
|
|
|
|
if ((entry->repeatCount > runLimit) |
|
|
|
|
|
|| (previous->repeatCount > (runLimit - entry->repeatCount)) |
|
|
|
|
|
|| (previous->repeatCount |
|
|
|
|
|
> (0xFFFFFFFFUL - entry->repeatCount))) |
|
|
|
|
|
{ |
|
|
|
|
|
return 0U; |
|
|
|
|
|
} |
|
|
|
|
|
previous->repeatCount += entry->repeatCount; |
|
|
|
|
|
return 1U; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
static uint8_t PlsrProfileQueueAppendLocked( |
|
|
static uint8_t PlsrProfileQueueAppendLocked( |
|
|
PLSR_PROFILE_QUEUE *queue, |
|
|
PLSR_PROFILE_QUEUE *queue, |
|
|
const PLSR_PROFILE_ENTRY *entry) |
|
|
const PLSR_PROFILE_ENTRY *entry) |
|
|
@@ -2679,20 +2708,8 @@ static uint8_t PlsrProfileQueueAppendLocked( |
|
|
{ |
|
|
{ |
|
|
previous = &queue->entries[ |
|
|
previous = &queue->entries[ |
|
|
(writeIndex - 1UL) & PLSR_PROFILE_QUEUE_MASK]; |
|
|
(writeIndex - 1UL) & PLSR_PROFILE_QUEUE_MASK]; |
|
|
if ((previous->startsNextSegment == 0U) |
|
|
|
|
|
&& (entry->startsNextSegment == 0U) |
|
|
|
|
|
&& (PlsrTimerSettingsEqual( |
|
|
|
|
|
&previous->setting, &entry->setting) != 0U) |
|
|
|
|
|
&& (entry->repeatCount <= PlsrProfileRunLimit( |
|
|
|
|
|
previous->setting.actualFrequencyHz)) |
|
|
|
|
|
&& (previous->repeatCount |
|
|
|
|
|
<= (PlsrProfileRunLimit( |
|
|
|
|
|
previous->setting.actualFrequencyHz) |
|
|
|
|
|
- entry->repeatCount)) |
|
|
|
|
|
&& (previous->repeatCount |
|
|
|
|
|
<= (0xFFFFFFFFUL - entry->repeatCount))) |
|
|
|
|
|
{ |
|
|
|
|
|
previous->repeatCount += entry->repeatCount; |
|
|
|
|
|
|
|
|
if (PlsrProfileEntryTryMerge(previous, entry) != 0U) |
|
|
|
|
|
{ |
|
|
return 1U; |
|
|
return 1U; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@@ -3145,21 +3162,33 @@ static uint32_t PlsrProfileTimingNow(void) |
|
|
return *((volatile uint32_t *)0xE0001004UL); |
|
|
return *((volatile uint32_t *)0xE0001004UL); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static void PlsrProfileRecordProducerCycles(uint32_t startCycles) |
|
|
|
|
|
|
|
|
static void PlsrProfileRecordProducerCycles(uint32_t startCycles, |
|
|
|
|
|
uint16_t itemCount) |
|
|
{ |
|
|
{ |
|
|
uint32_t elapsed = PlsrProfileTimingNow() - startCycles; |
|
|
uint32_t elapsed = PlsrProfileTimingNow() - startCycles; |
|
|
uint32_t total = PlsrProfileProducerTotalCycles; |
|
|
uint32_t total = PlsrProfileProducerTotalCycles; |
|
|
|
|
|
uint32_t perItemCycles; |
|
|
|
|
|
|
|
|
if (PlsrProfileProducerItemCount != 0xFFFFFFFFUL) |
|
|
|
|
|
|
|
|
if (itemCount == 0U) |
|
|
{ |
|
|
{ |
|
|
PlsrProfileProducerItemCount++; |
|
|
|
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
if (PlsrProfileProducerItemCount |
|
|
|
|
|
<= (0xFFFFFFFFUL - (uint32_t)itemCount)) |
|
|
|
|
|
{ |
|
|
|
|
|
PlsrProfileProducerItemCount += itemCount; |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
PlsrProfileProducerItemCount = 0xFFFFFFFFUL; |
|
|
} |
|
|
} |
|
|
PlsrProfileProducerTotalCycles = |
|
|
PlsrProfileProducerTotalCycles = |
|
|
(elapsed > (0xFFFFFFFFUL - total)) ? 0xFFFFFFFFUL |
|
|
(elapsed > (0xFFFFFFFFUL - total)) ? 0xFFFFFFFFUL |
|
|
: total + elapsed; |
|
|
: total + elapsed; |
|
|
if (elapsed > PlsrProfileProducerMaxItemCycles) |
|
|
|
|
|
|
|
|
perItemCycles = (elapsed + itemCount - 1UL) / itemCount; |
|
|
|
|
|
if (perItemCycles > PlsrProfileProducerMaxItemCycles) |
|
|
{ |
|
|
{ |
|
|
PlsrProfileProducerMaxItemCycles = elapsed; |
|
|
|
|
|
|
|
|
PlsrProfileProducerMaxItemCycles = perItemCycles; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
#endif |
|
|
#endif |
|
|
@@ -3167,23 +3196,29 @@ static void PlsrProfileRecordProducerCycles(uint32_t startCycles) |
|
|
static uint8_t PlsrProfileQueueFill(uint16_t targetCount, |
|
|
static uint8_t PlsrProfileQueueFill(uint16_t targetCount, |
|
|
uint16_t *itemBudget) |
|
|
uint16_t *itemBudget) |
|
|
{ |
|
|
{ |
|
|
PLSR_SHORT_PROFILE candidate;//规划账本的工作副本。在临界区外算,算完再写回 |
|
|
|
|
|
PLSR_PROFILE_ENTRY entry;//这一圈要入队的那一项 |
|
|
|
|
|
|
|
|
PLSR_SHORT_PROFILE candidate; |
|
|
|
|
|
PLSR_PROFILE_ENTRY entries[PLSR_PROFILE_PRODUCER_BATCH_ITEMS]; |
|
|
|
|
|
PLSR_PROFILE_ENTRY entry; |
|
|
uint32_t generation; |
|
|
uint32_t generation; |
|
|
uint32_t producerEpoch;//拷出去时记下的版本,写回来要对得上 |
|
|
|
|
|
uint32_t criticalState;//开关中断用 |
|
|
|
|
|
uint32_t underrunDebt;//硬件已经「借用」了多少还没规划的脉冲 |
|
|
|
|
|
|
|
|
uint32_t producerEpoch; |
|
|
|
|
|
uint32_t criticalState; |
|
|
|
|
|
uint32_t underrunDebt; |
|
|
uint32_t queueReadIndex; |
|
|
uint32_t queueReadIndex; |
|
|
uint32_t queueWriteIndex; |
|
|
uint32_t queueWriteIndex; |
|
|
uint32_t queueCount;//当前深度 |
|
|
|
|
|
|
|
|
uint32_t queueCount; |
|
|
uint32_t currentGeneration; |
|
|
uint32_t currentGeneration; |
|
|
uint32_t currentProducerEpoch; |
|
|
uint32_t currentProducerEpoch; |
|
|
PLSR_PROFILE_QUEUE *queue;//队列指针 |
|
|
|
|
|
|
|
|
uint32_t targetSpace; |
|
|
|
|
|
PLSR_PROFILE_QUEUE *queue; |
|
|
|
|
|
uint16_t batchLimit; |
|
|
|
|
|
uint16_t generatedItems; |
|
|
|
|
|
uint16_t entryCount; |
|
|
|
|
|
uint16_t index; |
|
|
uint8_t queueBank; |
|
|
uint8_t queueBank; |
|
|
uint8_t currentBank;//当前活队列 |
|
|
|
|
|
|
|
|
uint8_t currentBank; |
|
|
uint8_t queueActive; |
|
|
uint8_t queueActive; |
|
|
uint8_t generatorComplete; |
|
|
uint8_t generatorComplete; |
|
|
uint8_t payingUnderrunDebt;//这一圈是还债还是真入 |
|
|
|
|
|
|
|
|
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; |
|
|
@@ -3191,57 +3226,90 @@ static uint8_t PlsrProfileQueueFill(uint16_t targetCount, |
|
|
|
|
|
|
|
|
if (itemBudget == NULL) |
|
|
if (itemBudget == NULL) |
|
|
{ |
|
|
{ |
|
|
//没有预算指针,没法扣次数,当失败 |
|
|
|
|
|
return 0U; |
|
|
return 0U; |
|
|
} |
|
|
} |
|
|
if ((targetCount == 0U) |
|
|
if ((targetCount == 0U) |
|
|
|| (targetCount > PLSR_PROFILE_QUEUE_CAPACITY)) |
|
|
|| (targetCount > PLSR_PROFILE_QUEUE_CAPACITY)) |
|
|
{ |
|
|
{ |
|
|
//非法目标就按整队列来。正常调用是 400/800,走不到这里 |
|
|
|
|
|
targetCount = PLSR_PROFILE_QUEUE_CAPACITY; |
|
|
targetCount = PLSR_PROFILE_QUEUE_CAPACITY; |
|
|
} |
|
|
} |
|
|
while (1) |
|
|
while (1) |
|
|
{ |
|
|
{ |
|
|
criticalState = PlsrPlatformEnterCritical();//进入临界区 |
|
|
|
|
|
queueBank = PlsrProfileQueueBank;//现在活着的那一套队列是 0 还是 1。先记下来 |
|
|
|
|
|
queue = &PlsrProfileQueues[queueBank];//拿到这套队列的指针。后面 queue->xxx 都是这套 |
|
|
|
|
|
queueActive = queue->active;//这套还在给当前运动供货吗。段停完/作废会变成 0 |
|
|
|
|
|
generatorComplete = queue->generatorComplete;//规划器是不是已经把这段所有脉冲都吐完了 |
|
|
|
|
|
queueWriteIndex = queue->writeIndex;//任务下一次要写的位置(累计值,不是 0~1023 的下标) |
|
|
|
|
|
queueReadIndex = queue->readIndex;//IRQ 下一次要读的位置 |
|
|
|
|
|
queueCount = queueWriteIndex - queueReadIndex;//当前有多少格。无符号减法,环形也能得到深度 |
|
|
|
|
|
if ((queueActive == 0U)//不供货了,不用再算 |
|
|
|
|
|
|| (generatorComplete != 0U)//这段已经吐完,不用再算 |
|
|
|
|
|
|| (queueCount >= targetCount)//已经够深(调用方一般是 800 或 400) |
|
|
|
|
|
|| (*itemBudget == 0U))//这一拍允许新算的次数用光了 |
|
|
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
//关闭临界区 |
|
|
|
|
|
|
|
|
#if defined(PLSR_DEBUG_TIMING) && (PLSR_DEBUG_TIMING != 0) \ |
|
|
|
|
|
&& !defined(PLSR_HOST_TEST) |
|
|
|
|
|
startCycles = PlsrProfileTimingNow(); |
|
|
|
|
|
#endif |
|
|
|
|
|
criticalState = PlsrPlatformEnterCritical(); |
|
|
|
|
|
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); |
|
|
PlsrPlatformExitCritical(criticalState); |
|
|
return 1U; |
|
|
return 1U; |
|
|
} |
|
|
} |
|
|
/*先扣一次「本拍还能算几项」。 |
|
|
|
|
|
哪怕后面这项被扔掉,次数也花掉了,防止一拍里死循环*/ |
|
|
|
|
|
(*itemBudget)--; |
|
|
|
|
|
generation = queue->generation;//记下现在的世代。改频、借债会 generation++ |
|
|
|
|
|
producerEpoch = queue->producerEpoch;//记下队列现在的世代。改频、借债会 generation++ |
|
|
|
|
|
underrunDebt = queue->underrunDebtPulses;//硬件已经用末频「预支」了多少脉冲,规划还没跟上 |
|
|
|
|
|
payingUnderrunDebt = (underrunDebt != 0UL) ? 1U : 0U;//这一圈是 还债 还是 入队。有债=1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
generation = queue->generation; |
|
|
|
|
|
producerEpoch = queue->producerEpoch; |
|
|
|
|
|
underrunDebt = queue->underrunDebtPulses; |
|
|
|
|
|
payingUnderrunDebt = (underrunDebt != 0UL) ? 1U : 0U; |
|
|
|
|
|
batchLimit = *itemBudget; |
|
|
|
|
|
if (batchLimit > PLSR_PROFILE_PRODUCER_BATCH_ITEMS) |
|
|
|
|
|
{ |
|
|
|
|
|
batchLimit = PLSR_PROFILE_PRODUCER_BATCH_ITEMS; |
|
|
|
|
|
} |
|
|
|
|
|
targetSpace = targetCount - queueCount; |
|
|
|
|
|
if ((uint32_t)batchLimit > targetSpace) |
|
|
|
|
|
{ |
|
|
|
|
|
batchLimit = (uint16_t)targetSpace; |
|
|
|
|
|
} |
|
|
|
|
|
if (payingUnderrunDebt != 0U) |
|
|
|
|
|
{ |
|
|
|
|
|
/* Debt changes the queue generation and must be paid against the |
|
|
|
|
|
latest published amount, so keep this exceptional path single. */ |
|
|
|
|
|
batchLimit = 1U; |
|
|
|
|
|
} |
|
|
PlsrCopyShortProfile(&candidate, &queue->producerProfile); |
|
|
PlsrCopyShortProfile(&candidate, &queue->producerProfile); |
|
|
//把规划账本拷到本地 candidate。等会儿在门外改它,不能边算边让 IRQ 看到半成品 |
|
|
|
|
|
PlsrPlatformExitCritical(criticalState); |
|
|
PlsrPlatformExitCritical(criticalState); |
|
|
|
|
|
|
|
|
#if defined(PLSR_DEBUG_TIMING) && (PLSR_DEBUG_TIMING != 0) \ |
|
|
|
|
|
&& !defined(PLSR_HOST_TEST) |
|
|
|
|
|
startCycles = PlsrProfileTimingNow(); |
|
|
|
|
|
#endif |
|
|
|
|
|
if (PlsrShortProfileTakeRunLimited( &candidate, &entry, |
|
|
|
|
|
(payingUnderrunDebt != 0U) ? underrunDebt : 0xFFFFFFFFUL) == 0U) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
generatedItems = 0U; |
|
|
|
|
|
entryCount = 0U; |
|
|
|
|
|
while ((generatedItems < batchLimit) |
|
|
|
|
|
&& (candidate.active != 0U)) |
|
|
|
|
|
{ |
|
|
|
|
|
if (PlsrShortProfileTakeRunLimited( |
|
|
|
|
|
&candidate, &entry, |
|
|
|
|
|
(payingUnderrunDebt != 0U) |
|
|
|
|
|
? underrunDebt : 0xFFFFFFFFUL) == 0U) |
|
|
|
|
|
{ |
|
|
|
|
|
return 0U; |
|
|
|
|
|
} |
|
|
|
|
|
generatedItems++; |
|
|
|
|
|
if ((entryCount == 0U) |
|
|
|
|
|
|| (PlsrProfileEntryTryMerge( |
|
|
|
|
|
&entries[entryCount - 1U], &entry) == 0U)) |
|
|
|
|
|
{ |
|
|
|
|
|
entries[entryCount] = entry; |
|
|
|
|
|
entryCount++; |
|
|
|
|
|
} |
|
|
|
|
|
if (payingUnderrunDebt != 0U) |
|
|
|
|
|
{ |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if ((generatedItems == 0U) || (entryCount == 0U)) |
|
|
{ |
|
|
{ |
|
|
return 0U; |
|
|
return 0U; |
|
|
} |
|
|
} |
|
|
//正常才继续 |
|
|
|
|
|
|
|
|
*itemBudget = (uint16_t)(*itemBudget - generatedItems); |
|
|
|
|
|
|
|
|
criticalState = PlsrPlatformEnterCritical(); |
|
|
criticalState = PlsrPlatformEnterCritical(); |
|
|
currentBank = PlsrProfileQueueBank; |
|
|
currentBank = PlsrProfileQueueBank; |
|
|
queueActive = queue->active; |
|
|
queueActive = queue->active; |
|
|
@@ -3254,29 +3322,38 @@ static uint8_t PlsrProfileQueueFill(uint16_t targetCount, |
|
|
|| (queueActive == 0U) |
|
|
|| (queueActive == 0U) |
|
|
|| (currentGeneration != generation) |
|
|
|| (currentGeneration != generation) |
|
|
|| (currentProducerEpoch != producerEpoch) |
|
|
|| (currentProducerEpoch != producerEpoch) |
|
|
|| (queueCount >= PLSR_PROFILE_QUEUE_CAPACITY)) |
|
|
|
|
|
|
|
|
|| ((payingUnderrunDebt == 0U) |
|
|
|
|
|
&& ((queueCount + entryCount) |
|
|
|
|
|
> PLSR_PROFILE_QUEUE_CAPACITY))) |
|
|
{ |
|
|
{ |
|
|
PlsrPlatformExitCritical(criticalState); |
|
|
PlsrPlatformExitCritical(criticalState); |
|
|
continue; |
|
|
continue; |
|
|
} |
|
|
} |
|
|
PlsrCopyShortProfile(&queue->producerProfile, &candidate); |
|
|
|
|
|
|
|
|
|
|
|
if (payingUnderrunDebt != 0U) |
|
|
if (payingUnderrunDebt != 0U) |
|
|
{ |
|
|
{ |
|
|
underrunDebt = queue->underrunDebtPulses; |
|
|
underrunDebt = queue->underrunDebtPulses; |
|
|
if (underrunDebt < entry.repeatCount) |
|
|
|
|
|
|
|
|
if (underrunDebt < entries[0].repeatCount) |
|
|
{ |
|
|
{ |
|
|
PlsrPlatformExitCritical(criticalState); |
|
|
PlsrPlatformExitCritical(criticalState); |
|
|
continue; |
|
|
continue; |
|
|
} |
|
|
} |
|
|
queue->underrunDebtPulses = |
|
|
queue->underrunDebtPulses = |
|
|
underrunDebt - entry.repeatCount; |
|
|
|
|
|
|
|
|
underrunDebt - entries[0].repeatCount; |
|
|
} |
|
|
} |
|
|
else if (PlsrProfileQueueAppendLocked( |
|
|
|
|
|
queue, &entry) == 0U) |
|
|
|
|
|
|
|
|
else |
|
|
{ |
|
|
{ |
|
|
PlsrPlatformExitCritical(criticalState); |
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
for (index = 0U; index < entryCount; index++) |
|
|
|
|
|
{ |
|
|
|
|
|
if (PlsrProfileQueueAppendLocked( |
|
|
|
|
|
queue, &entries[index]) == 0U) |
|
|
|
|
|
{ |
|
|
|
|
|
PlsrPlatformExitCritical(criticalState); |
|
|
|
|
|
return 0U; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
PlsrCopyShortProfile(&queue->producerProfile, &candidate); |
|
|
queueWriteIndex = queue->writeIndex; |
|
|
queueWriteIndex = queue->writeIndex; |
|
|
queueReadIndex = queue->readIndex; |
|
|
queueReadIndex = queue->readIndex; |
|
|
queueCount = queueWriteIndex - queueReadIndex; |
|
|
queueCount = queueWriteIndex - queueReadIndex; |
|
|
@@ -3293,7 +3370,7 @@ static uint8_t PlsrProfileQueueFill(uint16_t targetCount, |
|
|
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) |
|
|
PlsrProfileRecordProducerCycles(startCycles); |
|
|
|
|
|
|
|
|
PlsrProfileRecordProducerCycles(startCycles, generatedItems); |
|
|
#endif |
|
|
#endif |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@@ -3307,6 +3384,7 @@ static PLSR_PLATFORM_QUEUE_RESULT PlsrProfileQueueCommitNext( |
|
|
uint32_t writeIndex; |
|
|
uint32_t writeIndex; |
|
|
uint32_t actualFrequencyHz; |
|
|
uint32_t actualFrequencyHz; |
|
|
uint32_t repeatRemaining; |
|
|
uint32_t repeatRemaining; |
|
|
|
|
|
uint16_t queueDepth; |
|
|
PLSR_PLATFORM_QUEUE_RESULT result; |
|
|
PLSR_PLATFORM_QUEUE_RESULT result; |
|
|
|
|
|
|
|
|
repeatRemaining = PlsrProfileQueue.repeatRemaining; |
|
|
repeatRemaining = PlsrProfileQueue.repeatRemaining; |
|
|
@@ -3347,10 +3425,10 @@ static PLSR_PLATFORM_QUEUE_RESULT PlsrProfileQueueCommitNext( |
|
|
nextReadIndex = readIndex + 1UL; |
|
|
nextReadIndex = readIndex + 1UL; |
|
|
PlsrProfileQueue.readIndex = nextReadIndex; |
|
|
PlsrProfileQueue.readIndex = nextReadIndex; |
|
|
PlsrProfileQueue.repeatRemaining = entry->repeatCount - 1UL; |
|
|
PlsrProfileQueue.repeatRemaining = entry->repeatCount - 1UL; |
|
|
|
|
|
queueDepth = (uint16_t)(PlsrProfileQueue.writeIndex - nextReadIndex); |
|
|
if (PlsrProfileQueue.generatorComplete == 0U) |
|
|
if (PlsrProfileQueue.generatorComplete == 0U) |
|
|
{ |
|
|
{ |
|
|
PlsrProfileQueueRecordDepth( |
|
|
|
|
|
(uint16_t)(PlsrProfileQueue.writeIndex - nextReadIndex)); |
|
|
|
|
|
|
|
|
PlsrProfileQueueRecordDepth(queueDepth); |
|
|
} |
|
|
} |
|
|
PlsrDeferredFrequencyPending = 0U; |
|
|
PlsrDeferredFrequencyPending = 0U; |
|
|
PlsrQueuedFrequencyHz = actualFrequencyHz; |
|
|
PlsrQueuedFrequencyHz = actualFrequencyHz; |
|
|
@@ -6076,6 +6154,53 @@ static uint8_t PlsrServiceCountedExecutor(void) |
|
|
return 1U; |
|
|
return 1U; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
uint8_t PlsrServiceProfileProducer(uint16_t itemBudget) |
|
|
|
|
|
{ |
|
|
|
|
|
uint8_t executorMode; |
|
|
|
|
|
|
|
|
|
|
|
if ((PlsrInitialized == 0U) || (itemBudget == 0U)) |
|
|
|
|
|
{ |
|
|
|
|
|
return 0U; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
executorMode = PlsrExecutor.mode; |
|
|
|
|
|
if (((executorMode == PLSR_EXEC_STEP_TABLE) |
|
|
|
|
|
|| (executorMode == PLSR_EXEC_STREAM) |
|
|
|
|
|
|| (executorMode == PLSR_EXEC_AB_LEGACY)) |
|
|
|
|
|
&& (PlsrProfileQueue.active != 0U) |
|
|
|
|
|
&& (PlsrProfileQueue.generatorComplete == 0U) |
|
|
|
|
|
&& (PlsrProfileQueueFill(PLSR_PROFILE_REFILL_TARGET, |
|
|
|
|
|
&itemBudget) == 0U)) |
|
|
|
|
|
{ |
|
|
|
|
|
PlsrEnterError(PLSR_ERROR_TIMER); |
|
|
|
|
|
return 0U; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
executorMode = PlsrExecutor.mode; |
|
|
|
|
|
if (((executorMode == PLSR_EXEC_STEP_TABLE) |
|
|
|
|
|
|| (executorMode == PLSR_EXEC_STREAM)) |
|
|
|
|
|
&& ((PlsrStageCountedHandoff() == 0U) |
|
|
|
|
|
|| (PlsrProfileQueueFill(PLSR_PROFILE_REFILL_TARGET, |
|
|
|
|
|
&itemBudget) == 0U))) |
|
|
|
|
|
{ |
|
|
|
|
|
PlsrEnterError(PLSR_ERROR_TIMER); |
|
|
|
|
|
return 0U; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
executorMode = PlsrExecutor.mode; |
|
|
|
|
|
if (((executorMode == PLSR_EXEC_STEP_TABLE) |
|
|
|
|
|
|| (executorMode == PLSR_EXEC_STREAM) |
|
|
|
|
|
|| (executorMode == PLSR_EXEC_AB_LEGACY)) |
|
|
|
|
|
&& (PlsrProfileQueue.active != 0U) |
|
|
|
|
|
&& (PlsrProfileQueue.generatorComplete == 0U) |
|
|
|
|
|
&& (PlsrProfileQueueCount() < PLSR_PROFILE_REFILL_TARGET)) |
|
|
|
|
|
{ |
|
|
|
|
|
return 1U; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return 0U; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
void PlsrPoll1ms(void) |
|
|
void PlsrPoll1ms(void) |
|
|
{ |
|
|
{ |
|
|
uint8_t extLevel;//这一拍读到的 EXT 引脚电平。后面才用,这段没用 |
|
|
uint8_t extLevel;//这一拍读到的 EXT 引脚电平。后面才用,这段没用 |
|
|
@@ -6089,7 +6214,6 @@ void PlsrPoll1ms(void) |
|
|
uint32_t newTargetHz;//改频后的新目标。后面才用 |
|
|
uint32_t newTargetHz;//改频后的新目标。后面才用 |
|
|
uint32_t pulseDirReplanPulses = 0UL;//重规划还剩多少脉冲。先 0 |
|
|
uint32_t pulseDirReplanPulses = 0UL;//重规划还剩多少脉冲。先 0 |
|
|
uint32_t pollEpoch;//这一拍看到的段世代号,防止用过期段的 EXT/改频 |
|
|
uint32_t pollEpoch;//这一拍看到的段世代号,防止用过期段的 EXT/改频 |
|
|
uint16_t fillBudget = PLSR_PROFILE_REFILL_BUDGET;//这段会用。 本拍最多往队列里新算多少项。宏是 200 |
|
|
|
|
|
PLSR_PLATFORM_SERVICE_RESULT pulseDirReplanResult = |
|
|
PLSR_PLATFORM_SERVICE_RESULT pulseDirReplanResult = |
|
|
PLSR_PLATFORM_SERVICE_READY;//重规划结果,先当成功 |
|
|
PLSR_PLATFORM_SERVICE_READY;//重规划结果,先当成功 |
|
|
|
|
|
|
|
|
@@ -6098,6 +6222,17 @@ void PlsrPoll1ms(void) |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#if !defined(PLSR_HOST_TEST) |
|
|
|
|
|
/* Run the target throughput benchmark only while idle. It blocks task |
|
|
|
|
|
context while measuring but never starts pulse output. */ |
|
|
|
|
|
if ((PlsrPlannerBenchmarkRequest != 0UL) |
|
|
|
|
|
&& (PlsrIsBusy() == 0U)) |
|
|
|
|
|
{ |
|
|
|
|
|
PlsrPlannerBenchmarkService(); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
if (PlsrPollCommandMailbox() != 0U) |
|
|
if (PlsrPollCommandMailbox() != 0U) |
|
|
{ |
|
|
{ |
|
|
/*看邮箱有没有 START / STOP / CLEAR。 |
|
|
/*看邮箱有没有 START / STOP / CLEAR。 |
|
|
@@ -6135,43 +6270,6 @@ void PlsrPoll1ms(void) |
|
|
return;//这段波已经结束,本拍不要再补队列。Fill 是给还在跑的段用的 |
|
|
return;//这段波已经结束,本拍不要再补队列。Fill 是给还在跑的段用的 |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (PlsrProfileQueueCount() <= PLSR_PROFILE_LOW_WATER)//当前队列还剩多少 格子 |
|
|
|
|
|
{ |
|
|
|
|
|
//浅到 ≤100,本拍预算从 200 改成 400。急救,多炒一倍菜。 |
|
|
|
|
|
//没浅,仍是 200 |
|
|
|
|
|
fillBudget = PLSR_PROFILE_STARTUP_BUDGET; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (((PlsrExecutor.mode == PLSR_EXEC_STEP_TABLE) |
|
|
|
|
|
|| (PlsrExecutor.mode == PLSR_EXEC_STREAM) |
|
|
|
|
|
|| (PlsrExecutor.mode == PLSR_EXEC_AB_LEGACY))//这三种才会用 profile 队列。IDLE 不补 |
|
|
|
|
|
&& (PlsrProfileQueue.active != 0U)//当前 bank 还在给这段供货。段停完后 active 会被清掉 |
|
|
|
|
|
&& (PlsrProfileQueue.generatorComplete == 0U)//本段规划还没把所有脉冲吐完。吐完了第一次 Fill 没意义 |
|
|
|
|
|
&& (PlsrProfileQueueFill(PLSR_PROFILE_REFILL_TARGET,&fillBudget) == 0U)) |
|
|
|
|
|
/*去算、去塞,直到格子到 800 或预算用完。返回 0 = Generate/入队失败*/ |
|
|
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
PlsrEnterError(PLSR_ERROR_TIMER); |
|
|
|
|
|
return;//活队列补失败,当定时器/规划故障停机 |
|
|
|
|
|
} |
|
|
|
|
|
if (((PlsrExecutor.mode == PLSR_EXEC_STEP_TABLE) |
|
|
|
|
|
|| (PlsrExecutor.mode == PLSR_EXEC_STREAM))//这里 没有 AB_LEGACY。PULSE/DIR 的 counted 下一段才走这条 |
|
|
|
|
|
&& ((PlsrStageCountedHandoff() == 0U) |
|
|
|
|
|
/*本段已经 generatorComplete 时,把下一段预热的第一/第二 run 接到队尾。 |
|
|
|
|
|
还不到接的时候它返回 1(成功但空操作)。 |
|
|
|
|
|
返回 0 才是真失败(队列塞不下、handoff 非法等*/ |
|
|
|
|
|
|| (PlsrProfileQueueFill(PLSR_PROFILE_REFILL_TARGET, |
|
|
|
|
|
&fillBudget) == 0U))) |
|
|
|
|
|
/*handoff 成功或空操作之后,再用 剩下的预算 再填一次。 |
|
|
|
|
|
handoff 刚占了格子,或第一次没填满。 |
|
|
|
|
|
|
|
|
|
|
|
两个子条件用 或:handoff 失败 或者 第二次 Fill 失败,都进错误。 |
|
|
|
|
|
短路:handoff 已经失败,第二次 Fill 不会跑。*/ |
|
|
|
|
|
{ |
|
|
|
|
|
PlsrEnterError(PLSR_ERROR_TIMER);//接不上下一段或再补失败,同样停 |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* This work is bounded and targets only the inactive bank. It is done |
|
|
/* This work is bounded and targets only the inactive bank. It is done |
|
|
after servicing the live queue so preparation cannot starve output. */ |
|
|
after servicing the live queue so preparation cannot starve output. */ |
|
|
PlsrServiceTimedStartPreparation(); |
|
|
PlsrServiceTimedStartPreparation(); |
|
|
|