Port-ify microsecond_timer hardware implementation (#1964)

* move to folder

* extract hardware part of microsecond_timer

* dead ST forum link

* de-volatile

* simulator build

* guard
This commit is contained in:
Matthew Kennedy 2020-11-22 17:39:32 -08:00 committed by GitHub
parent 4f7ff102f2
commit 6334e5011f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 90 additions and 64 deletions

View File

@ -1,7 +1,8 @@
HW_LAYER_INC= $(PROJECT_DIR)/hw_layer $(PROJECT_DIR)/hw_layer/adc \ HW_LAYER_INC= $(PROJECT_DIR)/hw_layer $(PROJECT_DIR)/hw_layer/adc \
$(PROJECT_DIR)/hw_layer/digital_input \ $(PROJECT_DIR)/hw_layer/digital_input \
$(PROJECT_DIR)/hw_layer/digital_input/trigger $(PROJECT_DIR)/hw_layer/digital_input/trigger \
$(PROJECT_DIR)/hw_layer/microsecond_timer \
HW_INC = hw_layer/$(CPU_HWLAYER) \ HW_INC = hw_layer/$(CPU_HWLAYER) \
$(PROJECT_DIR)/hw_layer/ports $(PROJECT_DIR)/hw_layer/ports
@ -15,7 +16,7 @@ HW_LAYER_EMS = $(HW_LAYER_EGT) \
HW_LAYER_EMS_CPP = $(HW_LAYER_EGT_CPP) \ HW_LAYER_EMS_CPP = $(HW_LAYER_EGT_CPP) \
$(PROJECT_DIR)/hw_layer/pin_repository.cpp \ $(PROJECT_DIR)/hw_layer/pin_repository.cpp \
$(PROJECT_DIR)/hw_layer/microsecond_timer_gpt.cpp \ $(PROJECT_DIR)/hw_layer/microsecond_timer/microsecond_timer.cpp \
$(PROJECT_DIR)/hw_layer/digital_input/digital_input.cpp \ $(PROJECT_DIR)/hw_layer/digital_input/digital_input.cpp \
$(PROJECT_DIR)/hw_layer/digital_input/digital_input_icu.cpp \ $(PROJECT_DIR)/hw_layer/digital_input/digital_input_icu.cpp \
$(PROJECT_DIR)/hw_layer/digital_input/digital_input_exti.cpp \ $(PROJECT_DIR)/hw_layer/digital_input/digital_input_exti.cpp \

View File

@ -12,14 +12,10 @@
*/ */
#include "global.h" #include "global.h"
#include "os_access.h"
#include "microsecond_timer.h" #include "microsecond_timer.h"
#include "scheduler.h" #include "port_microsecond_timer.h"
#include "os_util.h"
// https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https%3a%2f%2fmy.st.com%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fcortex_mx_stm32%2fInterrupt%20on%20CEN%20bit%20setting%20in%20TIM7&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=474 #if EFI_PROD_CODE
#if EFI_PROD_CODE && HAL_USE_GPT
#include "periodic_task.h" #include "periodic_task.h"
#include "engine.h" #include "engine.h"
@ -39,20 +35,19 @@ EXTERN_ENGINE;
*/ */
uint32_t maxPrecisionCallbackDuration = 0; uint32_t maxPrecisionCallbackDuration = 0;
static volatile efitick_t lastSetTimerTimeNt; static efitick_t lastSetTimerTimeNt;
static int lastSetTimerValue; static bool isTimerPending = false;
static volatile bool isTimerPending = false;
static volatile int timerCallbackCounter = 0; static int timerCallbackCounter = 0;
static volatile int timerRestartCounter = 0; static int timerRestartCounter = 0;
static const char * msg; static const char * msg;
static char buff[32]; static char buff[32];
static int timerFreezeCounter = 0; static int timerFreezeCounter = 0;
static volatile int setHwTimerCounter = 0; static int setHwTimerCounter = 0;
static volatile bool hwStarted = false; static bool hwStarted = false;
/** /**
* sets the alarm to the specified number of microseconds from now. * sets the alarm to the specified number of microseconds from now.
@ -61,56 +56,47 @@ static volatile bool hwStarted = false;
void setHardwareSchedulerTimer(efitick_t nowNt, efitick_t setTimeNt) { void setHardwareSchedulerTimer(efitick_t nowNt, efitick_t setTimeNt) {
efiAssertVoid(OBD_PCM_Processor_Fault, hwStarted, "HW.started"); efiAssertVoid(OBD_PCM_Processor_Fault, hwStarted, "HW.started");
int32_t deltaTimeUs = NT2US((int32_t)setTimeNt - (int32_t)nowNt); // How many ticks in the future is this event?
auto timeDeltaNt = setTimeNt - nowNt;
setHwTimerCounter++; setHwTimerCounter++;
/** /**
* #259 BUG error: not positive deltaTimeUs * #259 BUG error: not positive deltaTimeNt
* Once in a while we night get an interrupt where we do not expect it * Once in a while we night get an interrupt where we do not expect it
*/ */
if (deltaTimeUs <= 0) { if (timeDeltaNt <= 0) {
timerFreezeCounter++; timerFreezeCounter++;
warning(CUSTOM_OBD_LOCAL_FREEZE, "local freeze cnt=%d", timerFreezeCounter); warning(CUSTOM_OBD_LOCAL_FREEZE, "local freeze cnt=%d", timerFreezeCounter);
} }
// We need the timer to fire after we return - 1 doesn't work as it may actually schedule in the past // We need the timer to fire after we return - 1 doesn't work as it may actually schedule in the past
if (deltaTimeUs < 2) { if (timeDeltaNt < US2NT(2)) {
deltaTimeUs = 2; timeDeltaNt = US2NT(2);
} }
if (deltaTimeUs >= TOO_FAR_INTO_FUTURE_US) { if (timeDeltaNt >= TOO_FAR_INTO_FUTURE_NT) {
// we are trying to set callback for too far into the future. This does not look right at all // we are trying to set callback for too far into the future. This does not look right at all
firmwareError(CUSTOM_ERR_TIMER_OVERFLOW, "setHardwareSchedulerTimer() too far: %d", deltaTimeUs); firmwareError(CUSTOM_ERR_TIMER_OVERFLOW, "setHardwareSchedulerTimer() too far: %d", timeDeltaNt);
return;
}
// If already set, reset the timer
if (GPTDEVICE.state == GPT_ONESHOT) {
gptStopTimerI(&GPTDEVICE);
}
if (GPTDEVICE.state != GPT_READY) {
firmwareError(CUSTOM_HW_TIMER, "HW timer state %d/%d", GPTDEVICE.state, setHwTimerCounter);
return; return;
} }
// Skip scheduling if there's a firmware error active
if (hasFirmwareError()) { if (hasFirmwareError()) {
return; return;
} }
// Start the timer // Do the actual hardware-specific timer set operation
gptStartOneShotI(&GPTDEVICE, deltaTimeUs); portSetHardwareSchedulerTimer(nowNt, setTimeNt);
lastSetTimerTimeNt = getTimeNowNt(); lastSetTimerTimeNt = getTimeNowNt();
lastSetTimerValue = deltaTimeUs;
isTimerPending = true; isTimerPending = true;
timerRestartCounter++; timerRestartCounter++;
} }
void globalTimerCallback(); void globalTimerCallback();
static void hwTimerCallback(GPTDriver*) { void portMicrosecondTimerCallback() {
timerCallbackCounter++; timerCallbackCounter++;
isTimerPending = false; isTimerPending = false;
@ -127,7 +113,7 @@ class MicrosecondTimerWatchdogController : public PeriodicTimerController {
efitick_t nowNt = getTimeNowNt(); efitick_t nowNt = getTimeNowNt();
if (nowNt >= lastSetTimerTimeNt + 2 * CORE_CLOCK) { if (nowNt >= lastSetTimerTimeNt + 2 * CORE_CLOCK) {
strcpy(buff, "no_event"); strcpy(buff, "no_event");
itoa10(&buff[8], lastSetTimerValue); itoa10(&buff[8], lastSetTimerTimeNt);
firmwareError(CUSTOM_ERR_SCHEDULING_ERROR, buff); firmwareError(CUSTOM_ERR_SCHEDULING_ERROR, buff);
return; return;
} }
@ -144,14 +130,6 @@ class MicrosecondTimerWatchdogController : public PeriodicTimerController {
static MicrosecondTimerWatchdogController watchdogControllerInstance; static MicrosecondTimerWatchdogController watchdogControllerInstance;
/*
* The specific 1MHz frequency is important here since 'setHardwareUsTimer' method takes microsecond parameter
* For any arbitrary frequency to work we would need an additional layer of conversion.
*/
static constexpr GPTConfig gpt5cfg = { 1000000, /* 1 MHz timer clock.*/
hwTimerCallback, /* Timer callback.*/
0, 0 };
static scheduling_s watchDogBuddy; static scheduling_s watchDogBuddy;
static void watchDogBuddyCallback(void*) { static void watchDogBuddyCallback(void*) {
@ -195,8 +173,8 @@ static void validateHardwareTimer() {
} }
void initMicrosecondTimer() { void initMicrosecondTimer() {
gptStart(&GPTDEVICE, &gpt5cfg); portInitMicrosecondTimer();
efiAssertVoid(CUSTOM_ERR_TIMER_STATE, GPTDEVICE.state == GPT_READY, "hw state");
hwStarted = true; hwStarted = true;
lastSetTimerTimeNt = getTimeNowNt(); lastSetTimerTimeNt = getTimeNowNt();

View File

@ -0,0 +1,45 @@
#include "global.h"
#include "port_microsecond_timer.h"
#if EFI_PROD_CODE && HAL_USE_GPT
void portSetHardwareSchedulerTimer(efitick_t nowNt, efitick_t setTimeNt) {
int32_t deltaTimeUs = NT2US((int32_t)setTimeNt - (int32_t)nowNt);
// If already set, reset the timer
if (GPTDEVICE.state == GPT_ONESHOT) {
gptStopTimerI(&GPTDEVICE);
}
if (GPTDEVICE.state != GPT_READY) {
firmwareError(CUSTOM_HW_TIMER, "HW timer state %d", GPTDEVICE.state);
return;
}
// Start the timer
gptStartOneShotI(&GPTDEVICE, deltaTimeUs);
}
static void hwTimerCallback(GPTDriver*) {
portMicrosecondTimerCallback();
}
/*
* The specific 1MHz frequency is important here since 'setHardwareUsTimer' method takes microsecond parameter
* For any arbitrary frequency to work we would need an additional layer of conversion.
*/
static constexpr GPTConfig gpt5cfg = { 1000000, /* 1 MHz timer clock.*/
hwTimerCallback, /* Timer callback.*/
0, 0 };
void portInitMicrosecondTimer() {
gptStart(&GPTDEVICE, &gpt5cfg);
efiAssertVoid(CUSTOM_ERR_TIMER_STATE, GPTDEVICE.state == GPT_READY, "hw state");
}
#endif // EFI_PROD_CODE
// This implementation just uses the generic port counter - this usually returns a count of CPU cycles since start
uint32_t getTimeNowLowerNt() {
return port_rt_get_counter_value();
}

View File

@ -0,0 +1,13 @@
/**
* This file defines the API for the microsecond timer that a port needs to implement
*
* Do not call these functions directly, they should only be called by microsecond_timer.cpp
*/
#pragma once
void portInitMicrosecondTimer();
void portSetHardwareSchedulerTimer(efitick_t nowNt, efitick_t setTimeNt);
// The port should call this callback when the timer expires
void portMicrosecondTimerCallback();

View File

@ -11,6 +11,7 @@ HW_LAYER_EMS_CPP += $(PROJECT_DIR)/hw_layer/ports/cypress/mpu_util.cpp \
$(PROJECT_DIR)/hw_layer/ports/cypress/cypress_pins.cpp \ $(PROJECT_DIR)/hw_layer/ports/cypress/cypress_pins.cpp \
$(PROJECT_DIR)/hw_layer/ports/cypress/cypress_common.cpp \ $(PROJECT_DIR)/hw_layer/ports/cypress/cypress_common.cpp \
$(PROJECT_DIR)/hw_layer/ports/cypress/backup_ram.cpp \ $(PROJECT_DIR)/hw_layer/ports/cypress/backup_ram.cpp \
$(PROJECT_DIR)/hw_layer/trigger_input_adc.cpp $(PROJECT_DIR)/hw_layer/trigger_input_adc.cpp \
$(PROJECT_DIR)/hw_layer/microsecond_timer/microsecond_timer_gpt.cpp \
HW_INC += $(PROJECT_DIR)/hw_layer/ports/cypress/serial_over_usb HW_INC += $(PROJECT_DIR)/hw_layer/ports/cypress/serial_over_usb

View File

@ -257,9 +257,4 @@ uintptr_t getFlashAddrSecondCopy() {
return FLASH_ADDR_SECOND_COPY; return FLASH_ADDR_SECOND_COPY;
} }
uint32_t getTimeNowLowerNt() {
return port_rt_get_counter_value();
}
#endif /* EFI_PROD_CODE */ #endif /* EFI_PROD_CODE */

View File

@ -10,4 +10,5 @@ HW_LAYER_EMS_CPP += $(PROJECT_DIR)/hw_layer/ports/kinetis/mpu_util.cpp \
$(PROJECT_DIR)/hw_layer/ports/kinetis/kinetis_pins.cpp \ $(PROJECT_DIR)/hw_layer/ports/kinetis/kinetis_pins.cpp \
$(PROJECT_DIR)/hw_layer/ports/kinetis/backup_ram.cpp \ $(PROJECT_DIR)/hw_layer/ports/kinetis/backup_ram.cpp \
$(PROJECT_DIR)/hw_layer/ports/kinetis/kinetis_common.cpp \ $(PROJECT_DIR)/hw_layer/ports/kinetis/kinetis_common.cpp \
$(PROJECT_DIR)/hw_layer/trigger_input_comp.cpp $(PROJECT_DIR)/hw_layer/trigger_input_comp.cpp \
$(PROJECT_DIR)/hw_layer/microsecond_timer/microsecond_timer_gpt.cpp \

View File

@ -262,9 +262,4 @@ uintptr_t getFlashAddrSecondCopy() {
return nullptr; return nullptr;
} }
uint32_t getTimeNowLowerNt() {
return port_rt_get_counter_value();
}
#endif /* EFI_PROD_CODE */ #endif /* EFI_PROD_CODE */

View File

@ -326,7 +326,3 @@ bool isValidSerialRxPin(brain_pin_e pin) {
} }
#endif /*EFI_AUX_SERIAL*/ #endif /*EFI_AUX_SERIAL*/
uint32_t getTimeNowLowerNt() {
return port_rt_get_counter_value();
}

View File

@ -5,7 +5,8 @@ HW_LAYER_EMS += $(PROJECT_DIR)/hw_layer/ports/stm32/serial_over_usb/usbcfg.c \
HW_LAYER_EMS_CPP += \ HW_LAYER_EMS_CPP += \
$(PROJECT_DIR)/hw_layer/ports/stm32/stm32_pins.cpp \ $(PROJECT_DIR)/hw_layer/ports/stm32/stm32_pins.cpp \
$(PROJECT_DIR)/hw_layer/ports/stm32/stm32_common.cpp \ $(PROJECT_DIR)/hw_layer/ports/stm32/stm32_common.cpp \
$(PROJECT_DIR)/hw_layer/ports/stm32/backup_ram.cpp $(PROJECT_DIR)/hw_layer/ports/stm32/backup_ram.cpp \
$(PROJECT_DIR)/hw_layer/microsecond_timer/microsecond_timer_gpt.cpp \
RUSEFIASM = $(PROJECT_DIR)/hw_layer/ports/stm32/rusEfiStartup.S RUSEFIASM = $(PROJECT_DIR)/hw_layer/ports/stm32/rusEfiStartup.S