From 1bcf4afb10434e8b1e1b73e3e402c48c75f213fc Mon Sep 17 00:00:00 2001 From: Damien George Date: Sun, 3 Feb 2019 23:00:44 +1100 Subject: [PATCH] stm32/systick: Make periodic systick callbacks use a cyclic func table. Instead of checking each callback (currently storage and dma) explicitly for each SysTick IRQ, use a simple circular function table indexed by the lower bits of the millisecond tick counter. This allows callbacks to be easily enabled/disabled at runtime, and scales well to a large number of callbacks. --- ports/stm32/dma.c | 23 +++++++++++++-- ports/stm32/dma.h | 14 --------- ports/stm32/stm32_it.c | 65 ------------------------------------------ ports/stm32/storage.c | 19 +++++++++++- ports/stm32/storage.h | 4 --- ports/stm32/systick.c | 33 +++++++++++++++++++++ ports/stm32/systick.h | 16 +++++++++++ 7 files changed, 88 insertions(+), 86 deletions(-) diff --git a/ports/stm32/dma.c b/ports/stm32/dma.c index 9817bf6c15..b17c46068e 100644 --- a/ports/stm32/dma.c +++ b/ports/stm32/dma.c @@ -29,9 +29,16 @@ #include #include "py/obj.h" +#include "systick.h" #include "dma.h" #include "irq.h" +#define DMA_IDLE_ENABLED() (dma_idle.enabled != 0) +#define DMA_SYSTICK_LOG2 (3) +#define DMA_SYSTICK_MASK ((1 << DMA_SYSTICK_LOG2) - 1) +#define DMA_IDLE_TICK_MAX (8) // 8*8 = 64 msec +#define DMA_IDLE_TICK(tick) (((tick) & ~(SYSTICK_DISPATCH_NUM_SLOTS - 1) & DMA_SYSTICK_MASK) == 0) + typedef enum { dma_id_not_defined=-1, dma_id_0, @@ -52,6 +59,11 @@ typedef enum { dma_id_15, } dma_id_t; +typedef union { + uint16_t enabled; // Used to test if both counters are == 0 + uint8_t counter[2]; +} dma_idle_count_t; + struct _dma_descr_t { #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) DMA_Stream_TypeDef *instance; @@ -530,9 +542,12 @@ void DMA2_Channel7_IRQHandler(void) { IRQ_ENTER(DMA2_Channel7_IRQn); if (dma_han #endif +static void dma_idle_handler(uint32_t tick); + // Resets the idle counter for the DMA controller associated with dma_id. static void dma_tickle(dma_id_t dma_id) { dma_idle.counter[(dma_id < NSTREAMS_PER_CONTROLLER) ? 0 : 1] = 1; + systick_enable_dispatch(SYSTICK_DISPATCH_DMA, dma_idle_handler); } static void dma_enable_clock(dma_id_t dma_id) { @@ -681,12 +696,16 @@ void dma_invalidate_channel(const dma_descr_t *dma_descr) { // Called from the SysTick handler // We use LSB of tick to select which controller to process -void dma_idle_handler(int tick) { +static void dma_idle_handler(uint32_t tick) { + if (!DMA_IDLE_ENABLED() || !DMA_IDLE_TICK(tick)) { + return; + } + static const uint32_t controller_mask[] = { DMA1_ENABLE_MASK, DMA2_ENABLE_MASK }; { - int controller = tick & 1; + int controller = (tick >> DMA_SYSTICK_LOG2) & 1; if (dma_idle.counter[controller] == 0) { return; } diff --git a/ports/stm32/dma.h b/ports/stm32/dma.h index 84875374b2..73cb9c3282 100644 --- a/ports/stm32/dma.h +++ b/ports/stm32/dma.h @@ -81,23 +81,9 @@ extern const dma_descr_t dma_SDIO_0; #endif -typedef union { - uint16_t enabled; // Used to test if both counters are == 0 - uint8_t counter[2]; -} dma_idle_count_t; -extern volatile dma_idle_count_t dma_idle; -#define DMA_IDLE_ENABLED() (dma_idle.enabled != 0) - -#define DMA_SYSTICK_MASK 0x0e -#define DMA_MSECS_PER_SYSTICK (DMA_SYSTICK_MASK + 1) -#define DMA_IDLE_TICK_MAX (8) // 128 msec -#define DMA_IDLE_TICK(tick) (((tick) & DMA_SYSTICK_MASK) == 0) - - void dma_init(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint32_t dir, void *data); void dma_init_handle(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint32_t dir, void *data); void dma_deinit(const dma_descr_t *dma_descr); void dma_invalidate_channel(const dma_descr_t *dma_descr); -void dma_idle_handler(int controller); #endif // MICROPY_INCLUDED_STM32_DMA_H diff --git a/ports/stm32/stm32_it.c b/ports/stm32/stm32_it.c index 026082eb98..49b8ff1a8b 100644 --- a/ports/stm32/stm32_it.c +++ b/ports/stm32/stm32_it.c @@ -299,54 +299,6 @@ void PendSV_Handler(void) { pendsv_isr_handler(); } -/** - * @brief This function handles SysTick Handler. - * @param None - * @retval None - */ -void SysTick_Handler(void) { - // Instead of calling HAL_IncTick we do the increment here of the counter. - // This is purely for efficiency, since SysTick is called 1000 times per - // second at the highest interrupt priority. - // Note: we don't need uwTick to be declared volatile here because this is - // the only place where it can be modified, and the code is more efficient - // without the volatile specifier. - extern uint32_t uwTick; - uwTick += 1; - - // Read the systick control regster. This has the side effect of clearing - // the COUNTFLAG bit, which makes the logic in mp_hal_ticks_us - // work properly. - SysTick->CTRL; - - // Right now we have the storage and DMA controllers to process during - // this interrupt and we use custom dispatch handlers. If this needs to - // be generalised in the future then a dispatch table can be used as - // follows: ((void(*)(void))(systick_dispatch[uwTick & 0xf]))(); - - #if MICROPY_HW_ENABLE_STORAGE - if (STORAGE_IDLE_TICK(uwTick)) { - NVIC->STIR = FLASH_IRQn; - } - #endif - - if (DMA_IDLE_ENABLED() && DMA_IDLE_TICK(uwTick)) { - dma_idle_handler(uwTick); - } - - #if MICROPY_PY_THREAD - if (pyb_thread_enabled) { - if (pyb_thread_cur->timeslice == 0) { - if (pyb_thread_cur->run_next != pyb_thread_cur) { - SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; - } - } else { - --pyb_thread_cur->timeslice; - } - } - #endif -} - /******************************************************************************/ /* STM32F4xx Peripherals Interrupt Handlers */ /* Add here the Interrupt Handler for the used peripheral(s) (PPP), for the */ @@ -465,23 +417,6 @@ void OTG_HS_WKUP_IRQHandler(void) { { }*/ -// Handle a flash (erase/program) interrupt. -void FLASH_IRQHandler(void) { - IRQ_ENTER(FLASH_IRQn); - // This calls the real flash IRQ handler, if needed - /* - uint32_t flash_cr = FLASH->CR; - if ((flash_cr & FLASH_IT_EOP) || (flash_cr & FLASH_IT_ERR)) { - HAL_FLASH_IRQHandler(); - } - */ - #if MICROPY_HW_ENABLE_STORAGE - // This call the storage IRQ handler, to check if the flash cache needs flushing - storage_irq_handler(); - #endif - IRQ_EXIT(FLASH_IRQn); -} - /** * @brief These functions handle the EXTI interrupt requests. * @param None diff --git a/ports/stm32/storage.c b/ports/stm32/storage.c index 7724ae0f42..b150d73763 100644 --- a/ports/stm32/storage.c +++ b/ports/stm32/storage.c @@ -30,12 +30,16 @@ #include "py/runtime.h" #include "extmod/vfs_fat.h" +#include "systick.h" #include "led.h" #include "storage.h" #include "irq.h" #if MICROPY_HW_ENABLE_STORAGE +#define STORAGE_SYSTICK_MASK (0x1ff) // 512ms +#define STORAGE_IDLE_TICK(tick) (((tick) & ~(SYSTICK_DISPATCH_NUM_SLOTS - 1) & STORAGE_SYSTICK_MASK) == 0) + #define FLASH_PART1_START_BLOCK (0x100) #if defined(MICROPY_HW_BDEV2_IOCTL) @@ -44,10 +48,14 @@ static bool storage_is_initialised = false; +static void storage_systick_callback(uint32_t ticks_ms); + void storage_init(void) { if (!storage_is_initialised) { storage_is_initialised = true; + systick_enable_dispatch(SYSTICK_DISPATCH_STORAGE, storage_systick_callback); + MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_INIT, 0); #if defined(MICROPY_HW_BDEV2_IOCTL) @@ -73,11 +81,20 @@ uint32_t storage_get_block_count(void) { #endif } -void storage_irq_handler(void) { +static void storage_systick_callback(uint32_t ticks_ms) { + if (STORAGE_IDLE_TICK(ticks_ms)) { + // Trigger a FLASH IRQ to execute at a lower priority + NVIC->STIR = FLASH_IRQn; + } +} + +void FLASH_IRQHandler(void) { + IRQ_ENTER(FLASH_IRQn); MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_IRQ_HANDLER, 0); #if defined(MICROPY_HW_BDEV2_IOCTL) MICROPY_HW_BDEV2_IOCTL(BDEV_IOCTL_IRQ_HANDLER, 0); #endif + IRQ_EXIT(FLASH_IRQn); } void storage_flush(void) { diff --git a/ports/stm32/storage.h b/ports/stm32/storage.h index 7250b6dbf9..826b465a66 100644 --- a/ports/stm32/storage.h +++ b/ports/stm32/storage.h @@ -30,9 +30,6 @@ #define FLASH_BLOCK_SIZE (512) -#define STORAGE_SYSTICK_MASK (0x1ff) // 512ms -#define STORAGE_IDLE_TICK(tick) (((tick) & STORAGE_SYSTICK_MASK) == 2) - // Try to match Python-level VFS block protocol where possible for these constants enum { BDEV_IOCTL_INIT = 1, @@ -44,7 +41,6 @@ enum { void storage_init(void); uint32_t storage_get_block_size(void); uint32_t storage_get_block_count(void); -void storage_irq_handler(void); void storage_flush(void); bool storage_read_block(uint8_t *dest, uint32_t block); bool storage_write_block(const uint8_t *src, uint32_t block); diff --git a/ports/stm32/systick.c b/ports/stm32/systick.c index 8b5efe0577..d92f9d75dc 100644 --- a/ports/stm32/systick.c +++ b/ports/stm32/systick.c @@ -32,6 +32,39 @@ extern __IO uint32_t uwTick; +systick_dispatch_t systick_dispatch_table[SYSTICK_DISPATCH_NUM_SLOTS]; + +void SysTick_Handler(void) { + // Instead of calling HAL_IncTick we do the increment here of the counter. + // This is purely for efficiency, since SysTick is called 1000 times per + // second at the highest interrupt priority. + uint32_t uw_tick = uwTick + 1; + uwTick = uw_tick; + + // Read the systick control regster. This has the side effect of clearing + // the COUNTFLAG bit, which makes the logic in mp_hal_ticks_us + // work properly. + SysTick->CTRL; + + // Dispatch to any registered handlers in a cycle + systick_dispatch_t f = systick_dispatch_table[uw_tick & (SYSTICK_DISPATCH_NUM_SLOTS - 1)]; + if (f != NULL) { + f(uw_tick); + } + + #if MICROPY_PY_THREAD + if (pyb_thread_enabled) { + if (pyb_thread_cur->timeslice == 0) { + if (pyb_thread_cur->run_next != pyb_thread_cur) { + SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; + } + } else { + --pyb_thread_cur->timeslice; + } + } + #endif +} + // We provide our own version of HAL_Delay that calls __WFI while waiting, // and works when interrupts are disabled. This function is intended to be // used only by the ST HAL functions. diff --git a/ports/stm32/systick.h b/ports/stm32/systick.h index abb8729f54..d1b59c574b 100644 --- a/ports/stm32/systick.h +++ b/ports/stm32/systick.h @@ -26,6 +26,22 @@ #ifndef MICROPY_INCLUDED_STM32_SYSTICK_H #define MICROPY_INCLUDED_STM32_SYSTICK_H +#define SYSTICK_DISPATCH_NUM_SLOTS (2) +#define SYSTICK_DISPATCH_DMA (0) +#define SYSTICK_DISPATCH_STORAGE (1) + +typedef void (*systick_dispatch_t)(uint32_t); + +extern systick_dispatch_t systick_dispatch_table[SYSTICK_DISPATCH_NUM_SLOTS]; + +static inline void systick_enable_dispatch(size_t slot, systick_dispatch_t f) { + systick_dispatch_table[slot] = f; +} + +static inline void systick_disable_dispatch(size_t slot) { + systick_dispatch_table[slot] = NULL; +} + void systick_wait_at_least(uint32_t stc, uint32_t delay_ms); bool systick_has_passed(uint32_t stc, uint32_t delay_ms);