diff --git a/.changesets/scheduler-get-perf.md b/.changesets/scheduler-get-perf.md new file mode 100644 index 000000000..25590042d --- /dev/null +++ b/.changesets/scheduler-get-perf.md @@ -0,0 +1,2 @@ +release: minor +summary: Implement simple performance gathering with the scheduler diff --git a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp index 680bf82bf..c22a05a4d 100644 --- a/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp +++ b/Inc/HALAL/Models/TimerDomain/TimerDomain.hpp @@ -209,6 +209,26 @@ struct TimerPin { ST_LIB::TimerChannel channel; }; +struct TimerOutputChannelConfig { + uint32_t OCMode; /*!< Specifies the TIM mode. + This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */ + + uint32_t OCPolarity; /*!< Specifies the output polarity. + This parameter can be a value of @ref TIM_Output_Compare_Polarity */ + + uint32_t OCNPolarity; /*!< Specifies the complementary output polarity. + This parameter can be a value of @ref TIM_Output_Compare_N_Polarity + @note This parameter is valid only for timer instances supporting break feature. */ + + uint32_t OCIdleState; /*!< Specifies the TIM Output Compare pin state during Idle state. + This parameter can be a value of @ref TIM_Output_Compare_Idle_State + @note This parameter is valid only for timer instances supporting break feature. */ + + uint32_t OCNIdleState; /*!< Specifies the TIM Output Compare pin state during Idle state. + This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State + @note This parameter is valid only for timer instances supporting break feature. */ +}; + #ifndef DEFAULT_PWM_FREQUENCY_MODE #define DEFAULT_PWM_FREQUENCY_MODE ST_LIB::PWM_Frequency_Mode::PRECISION #endif diff --git a/Inc/HALAL/Services/Communication/UART/UART.hpp b/Inc/HALAL/Services/Communication/UART/UART.hpp index d3ac241b5..164f48c86 100644 --- a/Inc/HALAL/Services/Communication/UART/UART.hpp +++ b/Inc/HALAL/Services/Communication/UART/UART.hpp @@ -160,6 +160,16 @@ class UART { */ static bool transmit_polling(uint8_t id, span data); + /**@brief Transmits size bytes by polling. + * + * @param id Id of the UART + * @param data Pointer to data to be sent. + * @param length Length of data to be sent. + * @return bool Returns true if the packet has been send successfully. + * Returns false if the UART is busy or a problem has occurred. + */ + static bool transmit_polling(uint8_t id, uint8_t* data, size_t length); + /** * @brief This method request the receive of size bytes * by DMA and interrupts. Thus the data should not be used until diff --git a/Inc/HALAL/Services/PWM/DualPWM.hpp b/Inc/HALAL/Services/PWM/DualPWM.hpp index 09de02378..a375f8875 100644 --- a/Inc/HALAL/Services/PWM/DualPWM.hpp +++ b/Inc/HALAL/Services/PWM/DualPWM.hpp @@ -49,14 +49,12 @@ class DualPWM { PANIC("Timer instance is not set for DualPWM"); return; } - TIM_OC_InitTypeDef sConfigOC = { + ST_LIB::TimerOutputChannelConfig sConfigOC = { .OCMode = TIM_OCMODE_PWM1, - .Pulse = 0, .OCPolarity = polarity, .OCNPolarity = negated_polarity, - .OCFastMode = TIM_OCFAST_DISABLE, .OCIdleState = TIM_OCIDLESTATE_RESET, .OCNIdleState = TIM_OCNIDLESTATE_RESET, }; @@ -274,6 +272,67 @@ class DualPWM { SET_BIT(timer->instance->tim->BDTR, TIM_BDTR_MOE); } } + + /* NOTE: fast mode is only supported in pwms */ + inline void enable_fast_mode() { + if constexpr (pin.channel == TimerChannel::CHANNEL_1 || pin.channel == TimerChannel::CHANNEL_1_NEGATED) { + SET_BIT(this->timer->instance->tim->CCMR1, TIM_CCMR1_OC1FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_2 || pin.channel == TimerChannel::CHANNEL_2_NEGATED) { + SET_BIT(this->timer->instance->tim->CCMR1, TIM_CCMR1_OC2FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_3 || pin.channel == TimerChannel::CHANNEL_3_NEGATED) { + SET_BIT(this->timer->instance->tim->CCMR2, TIM_CCMR2_OC3FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_4 || pin.channel == TimerChannel::CHANNEL_4_NEGATED) { + SET_BIT(this->timer->instance->tim->CCMR2, TIM_CCMR2_OC4FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_5) { + SET_BIT(this->timer->instance->tim->CCMR3, TIM_CCMR3_OC5FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_6) { + SET_BIT(this->timer->instance->tim->CCMR3, TIM_CCMR3_OC6FE); + } + + if constexpr (negated_pin.channel == TimerChannel::CHANNEL_1 || negated_pin.channel == TimerChannel::CHANNEL_1_NEGATED) { + SET_BIT(this->timer->instance->tim->CCMR1, TIM_CCMR1_OC1FE); + } else if constexpr (negated_pin.channel == TimerChannel::CHANNEL_2 || negated_pin.channel == TimerChannel::CHANNEL_2_NEGATED) { + SET_BIT(this->timer->instance->tim->CCMR1, TIM_CCMR1_OC2FE); + } else if constexpr (negated_pin.channel == TimerChannel::CHANNEL_3 || negated_pin.channel == TimerChannel::CHANNEL_3_NEGATED) { + SET_BIT(this->timer->instance->tim->CCMR2, TIM_CCMR2_OC3FE); + } else if constexpr (negated_pin.channel == TimerChannel::CHANNEL_4 || negated_pin.channel == TimerChannel::CHANNEL_4_NEGATED) { + SET_BIT(this->timer->instance->tim->CCMR2, TIM_CCMR2_OC4FE); + } else if constexpr (negated_pin.channel == TimerChannel::CHANNEL_5) { + SET_BIT(this->timer->instance->tim->CCMR3, TIM_CCMR3_OC5FE); + } else if constexpr (negated_pin.channel == TimerChannel::CHANNEL_6) { + SET_BIT(this->timer->instance->tim->CCMR3, TIM_CCMR3_OC6FE); + } + } + + inline void disable_fast_mode() { + if constexpr (pin.channel == TimerChannel::CHANNEL_1 || pin.channel == TimerChannel::CHANNEL_1_NEGATED) { + CLEAR_BIT(this->timer->instance->tim->CCMR1, TIM_CCMR1_OC1FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_2 || pin.channel == TimerChannel::CHANNEL_2_NEGATED) { + CLEAR_BIT(this->timer->instance->tim->CCMR1, TIM_CCMR1_OC2FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_3 || pin.channel == TimerChannel::CHANNEL_3_NEGATED) { + CLEAR_BIT(this->timer->instance->tim->CCMR2, TIM_CCMR2_OC3FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_4 || pin.channel == TimerChannel::CHANNEL_4_NEGATED) { + CLEAR_BIT(this->timer->instance->tim->CCMR2, TIM_CCMR2_OC4FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_5) { + CLEAR_BIT(this->timer->instance->tim->CCMR3, TIM_CCMR3_OC5FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_6) { + CLEAR_BIT(this->timer->instance->tim->CCMR3, TIM_CCMR3_OC6FE); + } + + if constexpr (negated_pin.channel == TimerChannel::CHANNEL_1 || negated_pin.channel == TimerChannel::CHANNEL_1_NEGATED) { + CLEAR_BIT(this->timer->instance->tim->CCMR1, TIM_CCMR1_OC1FE); + } else if constexpr (negated_pin.channel == TimerChannel::CHANNEL_2 || negated_pin.channel == TimerChannel::CHANNEL_2_NEGATED) { + CLEAR_BIT(this->timer->instance->tim->CCMR1, TIM_CCMR1_OC2FE); + } else if constexpr (negated_pin.channel == TimerChannel::CHANNEL_3 || negated_pin.channel == TimerChannel::CHANNEL_3_NEGATED) { + CLEAR_BIT(this->timer->instance->tim->CCMR2, TIM_CCMR2_OC3FE); + } else if constexpr (negated_pin.channel == TimerChannel::CHANNEL_4 || negated_pin.channel == TimerChannel::CHANNEL_4_NEGATED) { + CLEAR_BIT(this->timer->instance->tim->CCMR2, TIM_CCMR2_OC4FE); + } else if constexpr (negated_pin.channel == TimerChannel::CHANNEL_5) { + CLEAR_BIT(this->timer->instance->tim->CCMR3, TIM_CCMR3_OC5FE); + } else if constexpr (negated_pin.channel == TimerChannel::CHANNEL_6) { + CLEAR_BIT(this->timer->instance->tim->CCMR3, TIM_CCMR3_OC6FE); + } + } }; } // namespace ST_LIB diff --git a/Inc/HALAL/Services/PWM/PWM.hpp b/Inc/HALAL/Services/PWM/PWM.hpp index 79e6b7fd7..037012251 100644 --- a/Inc/HALAL/Services/PWM/PWM.hpp +++ b/Inc/HALAL/Services/PWM/PWM.hpp @@ -40,14 +40,12 @@ template class PWM { PANIC("Timer instance is not set for PWM"); return; } - TIM_OC_InitTypeDef sConfigOC = { + ST_LIB::TimerOutputChannelConfig sConfigOC = { .OCMode = TIM_OCMODE_PWM1, - .Pulse = 0, .OCPolarity = polarity, .OCNPolarity = negated_polarity, - .OCFastMode = TIM_OCFAST_DISABLE, .OCIdleState = TIM_OCIDLESTATE_RESET, .OCNIdleState = TIM_OCNIDLESTATE_RESET, }; @@ -153,6 +151,39 @@ template class PWM { inline uint32_t get_frequency() const { return *(this->frequency); } inline float get_duty_cycle() const { return *(this->duty_cycle); } + + /* NOTE: fast mode is only supported in pwms */ + inline void enable_fast_mode() { + if constexpr (pin.channel == TimerChannel::CHANNEL_1 || pin.channel == TimerChannel::CHANNEL_1_NEGATED) { + SET_BIT(this->timer->instance->tim->CCMR1, TIM_CCMR1_OC1FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_2 || pin.channel == TimerChannel::CHANNEL_2_NEGATED) { + SET_BIT(this->timer->instance->tim->CCMR1, TIM_CCMR1_OC2FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_3 || pin.channel == TimerChannel::CHANNEL_3_NEGATED) { + SET_BIT(this->timer->instance->tim->CCMR2, TIM_CCMR2_OC3FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_4 || pin.channel == TimerChannel::CHANNEL_4_NEGATED) { + SET_BIT(this->timer->instance->tim->CCMR2, TIM_CCMR2_OC4FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_5) { + SET_BIT(this->timer->instance->tim->CCMR3, TIM_CCMR3_OC5FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_6) { + SET_BIT(this->timer->instance->tim->CCMR3, TIM_CCMR3_OC6FE); + } + } + + inline void disable_fast_mode() { + if constexpr (pin.channel == TimerChannel::CHANNEL_1 || pin.channel == TimerChannel::CHANNEL_1_NEGATED) { + CLEAR_BIT(this->timer->instance->tim->CCMR1, TIM_CCMR1_OC1FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_2 || pin.channel == TimerChannel::CHANNEL_2_NEGATED) { + CLEAR_BIT(this->timer->instance->tim->CCMR1, TIM_CCMR1_OC2FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_3 || pin.channel == TimerChannel::CHANNEL_3_NEGATED) { + CLEAR_BIT(this->timer->instance->tim->CCMR2, TIM_CCMR2_OC3FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_4 || pin.channel == TimerChannel::CHANNEL_4_NEGATED) { + CLEAR_BIT(this->timer->instance->tim->CCMR2, TIM_CCMR2_OC4FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_5) { + CLEAR_BIT(this->timer->instance->tim->CCMR3, TIM_CCMR3_OC5FE); + } else if constexpr (pin.channel == TimerChannel::CHANNEL_6) { + CLEAR_BIT(this->timer->instance->tim->CCMR3, TIM_CCMR3_OC6FE); + } + } }; } // namespace ST_LIB diff --git a/Inc/HALAL/Services/Time/Scheduler.hpp b/Inc/HALAL/Services/Time/Scheduler.hpp index ceb16e198..af5fd6016 100644 --- a/Inc/HALAL/Services/Time/Scheduler.hpp +++ b/Inc/HALAL/Services/Time/Scheduler.hpp @@ -17,7 +17,7 @@ #endif #include "stm32h7xx_ll_tim_wrapper.h" -#include "HALAL/Models/Packets/Packet.hpp" +#include "HALAL/Services/Communication/UART/UART.hpp" #include #include @@ -27,6 +27,25 @@ #include "stm32h7xx_hal_tim.h" #endif +#ifndef SIM_ON +//#define SCHEDULER_GET_LAST_N_TASKS +#else +struct UART { + struct Peripheral { + uint8_t ignore0; + }; + uint8_t ignore1; +}; +#endif + +#if !defined(SCHEDULER_GET_LAST_N_TASKS) && !defined(SLOW_CHECK_USE_READY_BITMAP) +#define SLOW_CHECK_USE_READY_BITMAP +#endif + +#if !defined(SCHEDULER_GET_LAST_N_TASKS_COUNT) +#define SCHEDULER_GET_LAST_N_TASKS_COUNT 64 +#endif + extern TIM_TypeDef* Scheduler_global_timer; void Scheduler_global_timer_callback(void* raw); void Scheduler_start(void); @@ -38,8 +57,12 @@ struct Scheduler { // if it isn't it could theoretically be used as an id in set_timeout static constexpr uint32_t INVALID_ID = 2 * kMaxTasks; - // temporary, will be removed - [[deprecated]] static inline void start() {} + /* gets and checks the performance gathering requirements + * @param tim32bit: A timer or null if not gathering perf info (preferrably 32 bit) + * @return If the given arguments are correct + */ + static bool init_perf(TIM_TypeDef* tim32bit, UART::Peripheral* uart); + static void update(); static uint64_t get_global_tick(); diff --git a/Inc/HALAL/Services/Time/TimerWrapper.hpp b/Inc/HALAL/Services/Time/TimerWrapper.hpp index 051849bb4..e3879718d 100644 --- a/Inc/HALAL/Services/Time/TimerWrapper.hpp +++ b/Inc/HALAL/Services/Time/TimerWrapper.hpp @@ -564,7 +564,7 @@ template struct TimerWrapper { /* NOTE(vic): Both config_output_compare_channel and config_input_compare_channel * Could probably be done better if not using TIM_[OC/IC]_InitTypeDef structures */ template - inline void config_output_compare_channel(const TIM_OC_InitTypeDef* OC_Config) { + inline void config_output_compare_channel(const ST_LIB::TimerOutputChannelConfig* OC_Config) { if constexpr (!((ch == TimerChannel::CHANNEL_1) || (ch == TimerChannel::CHANNEL_2) || (ch == TimerChannel::CHANNEL_3) || (ch == TimerChannel::CHANNEL_4) || (ch == TimerChannel::CHANNEL_5) || (ch == TimerChannel::CHANNEL_6))) { @@ -700,19 +700,6 @@ template struct TimerWrapper { instance->tim->CCMR3 = tmpccmrx; } - if constexpr (ch == TimerChannel::CHANNEL_1) - instance->tim->CCR1 = OC_Config->Pulse; - else if constexpr (ch == TimerChannel::CHANNEL_2) - instance->tim->CCR2 = OC_Config->Pulse; - else if constexpr (ch == TimerChannel::CHANNEL_3) - instance->tim->CCR3 = OC_Config->Pulse; - else if constexpr (ch == TimerChannel::CHANNEL_4) - instance->tim->CCR4 = OC_Config->Pulse; - else if constexpr (ch == TimerChannel::CHANNEL_5) - instance->tim->CCR5 = OC_Config->Pulse; - else if constexpr (ch == TimerChannel::CHANNEL_6) - instance->tim->CCR6 = OC_Config->Pulse; - instance->tim->CCER = tmpccer; } diff --git a/Src/HALAL/Services/Communication/UART/UART.cpp b/Src/HALAL/Services/Communication/UART/UART.cpp index 7772626f1..a070a4a0d 100644 --- a/Src/HALAL/Services/Communication/UART/UART.cpp +++ b/Src/HALAL/Services/Communication/UART/UART.cpp @@ -64,6 +64,10 @@ bool UART::transmit_polling(uint8_t id, uint8_t data) { } bool UART::transmit_polling(uint8_t id, span data) { + return UART::transmit_polling(id, data.data(), data.size()); +} + +bool UART::transmit_polling(uint8_t id, uint8_t* data, size_t length) { if (not UART::registered_uart.contains(id)) return false; // TODO: Error handler @@ -72,7 +76,7 @@ bool UART::transmit_polling(uint8_t id, span data) { if ((handle->ErrorCode & TXBUSYMASK) == 1) return false; - if (HAL_UART_Transmit(handle, data.data(), data.size(), 10) != HAL_OK) { + if (HAL_UART_Transmit(handle, data, length, 10) != HAL_OK) { return false; // TODO: Warning, Error during transmision } diff --git a/Src/HALAL/Services/Time/Scheduler.cpp b/Src/HALAL/Services/Time/Scheduler.cpp index a01b1da53..62700dc11 100644 --- a/Src/HALAL/Services/Time/Scheduler.cpp +++ b/Src/HALAL/Services/Time/Scheduler.cpp @@ -31,8 +31,31 @@ uint64_t Scheduler::global_tick_us_{0}; uint32_t Scheduler::current_interval_us_{0}; uint16_t Scheduler::timeout_idx_{1}; +#if (SCHEDULER_GET_LAST_N_TASKS_COUNT & (SCHEDULER_GET_LAST_N_TASKS_COUNT - 1)) != 0 +// NOTE: Performance: this is to use an and instead of a mod when storing the data +#error SCHEDULER_GET_LAST_N_TASKS_COUNT must be a power of 2 +#endif + +#if defined(SLOW_CHECK_USE_READY_BITMAP) uint16_t failing_id = Scheduler::INVALID_ID; +#elif defined(SCHEDULER_GET_LAST_N_TASKS) +struct TaskTimeInfo { + uint16_t id; + uint32_t start_time; + uint32_t end_time; +}; +TaskTimeInfo time_info[SCHEDULER_GET_LAST_N_TASKS_COUNT]{}; +#if SCHEDULER_GET_LAST_N_TASKS_COUNT > 65536 +#error Use a uint32_t instead and remove these lines +#endif +uint16_t current_time_info{0}; + +// Must be a 32 bit timer and not be the same as the scheduler timer +TIM_TypeDef* perf_timer{nullptr}; +uint8_t uart_id{0}; +#endif + // ---------------------------- inline void Scheduler::global_timer_disable() { @@ -77,20 +100,53 @@ void Scheduler_start(void) { Scheduler::schedule_next_interval(); } +bool Scheduler::init_perf(TIM_TypeDef* tim32bit, UART::Peripheral* uart) { +#ifdef SCHEDULER_GET_LAST_N_TASKS + if (!tim32bit) { + return false; + } + + perf_timer = tim32bit; + + uart_id = UART::inscribe(*uart); + + return true; +#else + (void)tim32bit; + (void)uart; + return true; +#endif +} + void Scheduler::update() { +#ifdef SLOW_CHECK_USE_READY_BITMAP // NOTE: Only _one_ id will be shown per call to update() if (failing_id != Scheduler::INVALID_ID) [[unlikely]] { WARNING("Too slow, could not execute task %u in time", failing_id); failing_id = Scheduler::INVALID_ID; } +#elif defined(SCHEDULER_GET_LAST_N_TASKS) + current_time_info = 0; +#endif while (ready_bitmap_ != 0u) { uint32_t bit_index = static_cast(__builtin_ctz(ready_bitmap_)); Task& task = tasks_[bit_index]; +#if defined(SCHEDULER_GET_LAST_N_TASKS) + TaskTimeInfo* info = time_info + current_time_info; + info->id = bit_index; + info->start_time = perf_timer->CNT; +#endif + task.callback(); +#if defined(SCHEDULER_GET_LAST_N_TASKS) + info->end_time = perf_timer->CNT; + current_time_info = current_time_info + 1; +#endif + SchedLock(); CLEAR_BIT(ready_bitmap_, 1u << bit_index); if (!task.repeating) [[unlikely]] { @@ -98,6 +154,16 @@ void Scheduler::update() { } SchedUnlock(); } + +#if defined(SCHEDULER_GET_LAST_N_TASKS) + if (!UART::transmit_polling( + uart_id, + (uint8_t*)&time_info, + sizeof(TaskTimeInfo) * current_time_info + )) { + WARNING("UART Error while trying to transmit timing info"); + } +#endif } uint64_t Scheduler::get_global_tick() { @@ -250,9 +316,11 @@ inline void Scheduler::on_timer_update() { SchedLock(); pop_front(); // mark task as ready +#ifdef SLOW_CHECK_USE_READY_BITMAP if ((ready_bitmap_ & task_bit) != 0) [[unlikely]] { failing_id = candidate_id; } +#endif SET_BIT(ready_bitmap_, task_bit); if (task.repeating) [[likely]] { task.next_fire_us = static_cast(global_tick_us_ + task.period_us);