fome-fw/firmware/controllers/scheduling/single_timer_executor.cpp

186 lines
5.5 KiB
C++
Raw Normal View History

2015-07-10 06:01:56 -07:00
/**
* @file SingleTimerExecutor.cpp
*
2019-05-05 06:41:44 -07:00
* This class combines the powers of a 1MHz hardware timer from microsecond_timer.cpp
2015-07-10 06:01:56 -07:00
* and pending events queue event_queue.cpp
*
* As of version 2.6.x, ChibiOS tick-based kernel is not capable of scheduling events
* with the level of precision we need, and realistically it should not.
*
2019-05-05 06:41:44 -07:00
* Update: actually newer ChibiOS has tickless mode and what we have here is pretty much the same thing :)
* open question if rusEfi should simply migrate to ChibiOS tickless scheduling (which would increase coupling with ChibiOS)
*
* See https://rusefi.com/forum/viewtopic.php?f=5&t=373&start=360#p30895
2018-12-24 16:36:03 -08:00
* for some performance data: with 'debug' firmware we spend about 5% of CPU in TIM5 handler which seem to be executed
* about 1500 times a second
*
2015-07-10 06:01:56 -07:00
* http://sourceforge.net/p/rusefi/tickets/24/
*
* @date: Apr 18, 2014
2018-01-20 17:55:31 -08:00
* @author Andrey Belomutskiy, (c) 2012-2018
2015-07-10 06:01:56 -07:00
*/
2019-07-05 17:03:32 -07:00
#include "global.h"
#include "os_access.h"
2019-03-29 06:11:13 -07:00
#include "single_timer_executor.h"
2015-07-10 06:01:56 -07:00
#include "efitime.h"
2015-07-10 06:01:56 -07:00
2019-04-12 19:07:03 -07:00
#if EFI_SIGNAL_EXECUTOR_ONE_TIMER
2015-07-10 06:01:56 -07:00
#include "microsecond_timer.h"
2017-06-07 19:55:05 -07:00
#include "tunerstudio_configuration.h"
2015-07-10 06:01:56 -07:00
#include "rfiutil.h"
#include "engine.h"
EXTERN_ENGINE;
2015-07-10 06:01:56 -07:00
extern schfunc_t globalTimerCallback;
//static int timerIsLate = 0;
//static efitime_t callbackTime = 0;
/**
* these fields are global in order to facilitate debugging
*/
static efitime_t nextEventTimeNt = 0;
2017-05-21 07:25:35 -07:00
uint32_t hwSetTimerDuration;
2015-07-10 06:01:56 -07:00
uint32_t lastExecutionCount;
static void executorCallback(void *arg) {
(void)arg;
2019-02-23 09:33:49 -08:00
efiAssertVoid(CUSTOM_ERR_6624, getCurrentRemainingStack() > 256, "lowstck#2y");
2015-07-10 06:01:56 -07:00
// callbackTime = getTimeNowNt();
// if((callbackTime > nextEventTimeNt) && (callbackTime - nextEventTimeNt > US2NT(5000))) {
// timerIsLate++;
// }
2019-01-27 23:59:14 -08:00
___engine.executor.onTimerCallback();
2015-07-10 06:01:56 -07:00
}
SingleTimerExecutor::SingleTimerExecutor() {
2015-07-10 06:01:56 -07:00
reentrantFlag = false;
2017-06-07 19:55:05 -07:00
doExecuteCounter = scheduleCounter = timerCallbackCounter = 0;
2015-07-10 06:01:56 -07:00
/**
* todo: a good comment
*/
queue.setLateDelay(US2NT(100));
}
void SingleTimerExecutor::scheduleForLater(scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param) {
scheduleByTimestamp(scheduling, getTimeNowUs() + delayUs, callback, param);
}
2018-09-10 19:42:24 -07:00
/**
* @brief Schedule an event at specific delay after now
*
* Invokes event callback after the specified amount of time.
2018-09-10 19:42:24 -07:00
* callback would be executed either on ISR thread or current thread if we would need to execute right away
*
* @param [in, out] scheduling Data structure to keep this event in the collection.
* @param [in] delayUs the number of microseconds before the output signal immediate output if delay is zero.
* @param [in] dwell the number of ticks of output duration.
2018-09-10 19:42:24 -07:00
*/
void SingleTimerExecutor::scheduleByTimestamp(scheduling_s *scheduling, efitimeus_t timeUs, schfunc_t callback,
2015-07-10 06:01:56 -07:00
void *param) {
2017-06-07 19:55:05 -07:00
scheduleCounter++;
2017-05-25 11:51:21 -07:00
bool alreadyLocked = true;
2015-07-10 06:01:56 -07:00
if (!reentrantFlag) {
// this would guard the queue and disable interrupts
2017-05-25 11:51:21 -07:00
alreadyLocked = lockAnyContext();
2015-07-10 06:01:56 -07:00
}
2016-01-11 14:01:33 -08:00
bool needToResetTimer = queue.insertTask(scheduling, US2NT(timeUs), callback, param);
2015-07-10 06:01:56 -07:00
if (!reentrantFlag) {
doExecute();
if (needToResetTimer) {
scheduleTimerCallback();
}
2017-05-25 11:51:21 -07:00
if (!alreadyLocked)
unlockAnyContext();
2015-07-10 06:01:56 -07:00
}
}
void SingleTimerExecutor::onTimerCallback() {
2017-06-07 19:55:05 -07:00
timerCallbackCounter++;
2017-05-25 11:51:21 -07:00
bool alreadyLocked = lockAnyContext();
2015-07-10 06:01:56 -07:00
doExecute();
scheduleTimerCallback();
2017-05-25 11:51:21 -07:00
if (!alreadyLocked)
unlockAnyContext();
2015-07-10 06:01:56 -07:00
}
/*
* this private method is executed under lock
*/
void SingleTimerExecutor::doExecute() {
2017-06-07 19:55:05 -07:00
doExecuteCounter++;
2015-07-10 06:01:56 -07:00
/**
* Let's execute actions we should execute at this point.
* reentrantFlag takes care of the use case where the actions we are executing are scheduling
* further invocations
*/
reentrantFlag = true;
int shouldExecute = 1;
/**
* in real life it could be that while we executing listeners time passes and it's already time to execute
* next listeners.
* TODO: add a counter & figure out a limit of iterations?
*/
int totalExecuted = 0;
while (shouldExecute > 0) {
/**
* It's worth noting that that the actions might be adding new actions into the queue
*/
efitick_t nowNt = getTimeNowNt();
shouldExecute = queue.executeAll(nowNt);
totalExecuted += shouldExecute;
}
lastExecutionCount = totalExecuted;
if (!isLocked()) {
2017-05-21 07:15:57 -07:00
firmwareError(CUSTOM_ERR_LOCK_ISSUE, "Someone has stolen my lock");
2015-07-10 06:01:56 -07:00
return;
}
reentrantFlag = false;
}
/**
* This method is always invoked under a lock
*/
void SingleTimerExecutor::scheduleTimerCallback() {
2015-07-10 06:01:56 -07:00
/**
* Let's grab fresh time value
*/
efitick_t nowNt = getTimeNowNt();
nextEventTimeNt = queue.getNextEventTime(nowNt);
2018-07-25 20:03:04 -07:00
efiAssertVoid(CUSTOM_ERR_6625, nextEventTimeNt > nowNt, "setTimer constraint");
2015-07-10 06:01:56 -07:00
if (nextEventTimeNt == EMPTY_QUEUE)
return; // no pending events in the queue
int32_t hwAlarmTime = NT2US((int32_t)nextEventTimeNt - (int32_t)nowNt);
2019-05-07 16:32:08 -07:00
uint32_t beforeHwSetTimer = getTimeNowLowerNt();
2015-07-10 06:01:56 -07:00
setHardwareUsTimer(hwAlarmTime == 0 ? 1 : hwAlarmTime);
2019-05-07 16:32:08 -07:00
hwSetTimerDuration = getTimeNowLowerNt() - beforeHwSetTimer;
2015-07-10 06:01:56 -07:00
}
2019-02-27 14:12:52 -08:00
void initSingleTimerExecutorHardware(void) {
2015-07-10 06:01:56 -07:00
globalTimerCallback = executorCallback;
initMicrosecondTimer();
}
2019-04-12 19:07:03 -07:00
#if EFI_TUNER_STUDIO
2017-06-07 19:55:05 -07:00
extern TunerStudioOutputChannels tsOutputChannels;
#endif /* EFI_TUNER_STUDIO */
2017-06-07 19:55:05 -07:00
void executorStatistics() {
2017-06-07 20:04:56 -07:00
if (engineConfiguration->debugMode == DBG_EXECUTOR) {
2019-04-12 19:07:03 -07:00
#if EFI_TUNER_STUDIO && EFI_SIGNAL_EXECUTOR_ONE_TIMER
2019-01-27 23:59:14 -08:00
tsOutputChannels.debugIntField1 = ___engine.executor.timerCallbackCounter;
tsOutputChannels.debugIntField2 = ___engine.executor.doExecuteCounter;
tsOutputChannels.debugIntField3 = ___engine.executor.scheduleCounter;
#endif /* EFI_TUNER_STUDIO */
2017-06-07 19:55:05 -07:00
}
}
#endif /* EFI_SIGNAL_EXECUTOR_ONE_TIMER */
2015-07-10 06:01:56 -07:00