2015-07-10 06:01:56 -07:00
|
|
|
/**
|
|
|
|
* @file SingleTimerExecutor.cpp
|
|
|
|
*
|
|
|
|
* This class combines the powers of a 1MHz hardware timer from microsecond_timer.c
|
|
|
|
* 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.
|
|
|
|
*
|
2018-12-24 16:36:03 -08:00
|
|
|
* See https://rusefi.com/forum/viewtopic.php?f=5&t=373&sid=e285ebd9a5677a83235116007e8eb65d&start=360#p30895
|
|
|
|
* 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
|
|
|
*/
|
|
|
|
|
|
|
|
#include "SingleTimerExecutor.h"
|
|
|
|
#include "efitime.h"
|
|
|
|
#include "efilib2.h"
|
|
|
|
|
2019-01-08 21:25:45 -08:00
|
|
|
#if EFI_SIGNAL_EXECUTOR_ONE_TIMER || defined(__DOXYGEN__)
|
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"
|
|
|
|
|
2019-01-08 22:07:50 -08:00
|
|
|
#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;
|
2018-07-25 20:03:04 -07:00
|
|
|
efiAssertVoid(CUSTOM_ERR_6624, getRemainingStack(chThdGetSelfX()) > 256, "lowstck#2y");
|
2015-07-10 06:01:56 -07:00
|
|
|
|
|
|
|
// callbackTime = getTimeNowNt();
|
|
|
|
// if((callbackTime > nextEventTimeNt) && (callbackTime - nextEventTimeNt > US2NT(5000))) {
|
|
|
|
// timerIsLate++;
|
|
|
|
// }
|
|
|
|
|
2019-01-08 22:07:50 -08:00
|
|
|
_engine.executor.onTimerCallback();
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2019-01-08 22:07:50 -08: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));
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
*/
|
2019-01-08 22:07:50 -08: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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-08 22:07:50 -08: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
|
|
|
|
*/
|
2019-01-08 22:07:50 -08:00
|
|
|
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
|
|
|
|
*/
|
2019-01-08 22:07:50 -08:00
|
|
|
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);
|
2017-05-21 07:25:35 -07:00
|
|
|
uint32_t beforeHwSetTimer = GET_TIMESTAMP();
|
2015-07-10 06:01:56 -07:00
|
|
|
setHardwareUsTimer(hwAlarmTime == 0 ? 1 : hwAlarmTime);
|
2017-05-21 07:25:35 -07:00
|
|
|
hwSetTimerDuration = GET_TIMESTAMP() - beforeHwSetTimer;
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-01-28 08:27:33 -08:00
|
|
|
* @brief Schedule an event at specific delay after now
|
2015-07-10 06:01:56 -07:00
|
|
|
*
|
|
|
|
* Invokes event callback after the specified amount of time.
|
|
|
|
*
|
|
|
|
* @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-01-28 08:27:33 -08:00
|
|
|
void scheduleForLater(scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param) {
|
2019-01-08 22:07:50 -08:00
|
|
|
_engine.executor.scheduleByTimestamp(scheduling, getTimeNowUs() + delayUs, callback, param);
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2018-01-28 08:27:33 -08:00
|
|
|
/**
|
|
|
|
* @brief Schedule an event at specified timestamp
|
|
|
|
*
|
|
|
|
* @param [in] timeUs absolute time of the event, since ECU boot
|
|
|
|
*/
|
|
|
|
void scheduleByTimestamp(scheduling_s *scheduling, efitimeus_t time, schfunc_t callback, void *param) {
|
2019-01-08 22:07:50 -08:00
|
|
|
_engine.executor.scheduleByTimestamp(scheduling, time, callback, param);
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void initSignalExecutorImpl(void) {
|
|
|
|
globalTimerCallback = executorCallback;
|
|
|
|
initMicrosecondTimer();
|
|
|
|
}
|
|
|
|
|
2018-11-16 04:40:06 -08:00
|
|
|
#if EFI_TUNER_STUDIO || defined(__DOXYGEN__)
|
2017-06-07 19:55:05 -07:00
|
|
|
extern TunerStudioOutputChannels tsOutputChannels;
|
2018-11-16 04:40:06 -08:00
|
|
|
#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-01-08 22:07:50 -08:00
|
|
|
#if (EFI_TUNER_STUDIO && EFI_SIGNAL_EXECUTOR_ONE_TIMER) || defined(__DOXYGEN__)
|
|
|
|
tsOutputChannels.debugIntField1 = _engine.executor.timerCallbackCounter;
|
|
|
|
tsOutputChannels.debugIntField2 = _engine.executor.doExecuteCounter;
|
|
|
|
tsOutputChannels.debugIntField3 = _engine.executor.scheduleCounter;
|
2018-11-16 04:40:06 -08:00
|
|
|
#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
|
|
|
|