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.
This commit is contained in:
Damien George 2019-02-03 23:00:44 +11:00
parent 5fbda53d3c
commit 1bcf4afb10
7 changed files with 88 additions and 86 deletions

View File

@ -29,9 +29,16 @@
#include <stdint.h> #include <stdint.h>
#include "py/obj.h" #include "py/obj.h"
#include "systick.h"
#include "dma.h" #include "dma.h"
#include "irq.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 { typedef enum {
dma_id_not_defined=-1, dma_id_not_defined=-1,
dma_id_0, dma_id_0,
@ -52,6 +59,11 @@ typedef enum {
dma_id_15, dma_id_15,
} dma_id_t; } 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 { struct _dma_descr_t {
#if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7)
DMA_Stream_TypeDef *instance; DMA_Stream_TypeDef *instance;
@ -530,9 +542,12 @@ void DMA2_Channel7_IRQHandler(void) { IRQ_ENTER(DMA2_Channel7_IRQn); if (dma_han
#endif #endif
static void dma_idle_handler(uint32_t tick);
// Resets the idle counter for the DMA controller associated with dma_id. // Resets the idle counter for the DMA controller associated with dma_id.
static void dma_tickle(dma_id_t dma_id) { static void dma_tickle(dma_id_t dma_id) {
dma_idle.counter[(dma_id < NSTREAMS_PER_CONTROLLER) ? 0 : 1] = 1; 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) { 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 // Called from the SysTick handler
// We use LSB of tick to select which controller to process // 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[] = { static const uint32_t controller_mask[] = {
DMA1_ENABLE_MASK, DMA2_ENABLE_MASK DMA1_ENABLE_MASK, DMA2_ENABLE_MASK
}; };
{ {
int controller = tick & 1; int controller = (tick >> DMA_SYSTICK_LOG2) & 1;
if (dma_idle.counter[controller] == 0) { if (dma_idle.counter[controller] == 0) {
return; return;
} }

View File

@ -81,23 +81,9 @@ extern const dma_descr_t dma_SDIO_0;
#endif #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(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_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_deinit(const dma_descr_t *dma_descr);
void dma_invalidate_channel(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 #endif // MICROPY_INCLUDED_STM32_DMA_H

View File

@ -299,54 +299,6 @@ void PendSV_Handler(void) {
pendsv_isr_handler(); 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 */ /* STM32F4xx Peripherals Interrupt Handlers */
/* Add here the Interrupt Handler for the used peripheral(s) (PPP), for the */ /* 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. * @brief These functions handle the EXTI interrupt requests.
* @param None * @param None

View File

@ -30,12 +30,16 @@
#include "py/runtime.h" #include "py/runtime.h"
#include "extmod/vfs_fat.h" #include "extmod/vfs_fat.h"
#include "systick.h"
#include "led.h" #include "led.h"
#include "storage.h" #include "storage.h"
#include "irq.h" #include "irq.h"
#if MICROPY_HW_ENABLE_STORAGE #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) #define FLASH_PART1_START_BLOCK (0x100)
#if defined(MICROPY_HW_BDEV2_IOCTL) #if defined(MICROPY_HW_BDEV2_IOCTL)
@ -44,10 +48,14 @@
static bool storage_is_initialised = false; static bool storage_is_initialised = false;
static void storage_systick_callback(uint32_t ticks_ms);
void storage_init(void) { void storage_init(void) {
if (!storage_is_initialised) { if (!storage_is_initialised) {
storage_is_initialised = true; storage_is_initialised = true;
systick_enable_dispatch(SYSTICK_DISPATCH_STORAGE, storage_systick_callback);
MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_INIT, 0); MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_INIT, 0);
#if defined(MICROPY_HW_BDEV2_IOCTL) #if defined(MICROPY_HW_BDEV2_IOCTL)
@ -73,11 +81,20 @@ uint32_t storage_get_block_count(void) {
#endif #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); MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_IRQ_HANDLER, 0);
#if defined(MICROPY_HW_BDEV2_IOCTL) #if defined(MICROPY_HW_BDEV2_IOCTL)
MICROPY_HW_BDEV2_IOCTL(BDEV_IOCTL_IRQ_HANDLER, 0); MICROPY_HW_BDEV2_IOCTL(BDEV_IOCTL_IRQ_HANDLER, 0);
#endif #endif
IRQ_EXIT(FLASH_IRQn);
} }
void storage_flush(void) { void storage_flush(void) {

View File

@ -30,9 +30,6 @@
#define FLASH_BLOCK_SIZE (512) #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 // Try to match Python-level VFS block protocol where possible for these constants
enum { enum {
BDEV_IOCTL_INIT = 1, BDEV_IOCTL_INIT = 1,
@ -44,7 +41,6 @@ enum {
void storage_init(void); void storage_init(void);
uint32_t storage_get_block_size(void); uint32_t storage_get_block_size(void);
uint32_t storage_get_block_count(void); uint32_t storage_get_block_count(void);
void storage_irq_handler(void);
void storage_flush(void); void storage_flush(void);
bool storage_read_block(uint8_t *dest, uint32_t block); bool storage_read_block(uint8_t *dest, uint32_t block);
bool storage_write_block(const uint8_t *src, uint32_t block); bool storage_write_block(const uint8_t *src, uint32_t block);

View File

@ -32,6 +32,39 @@
extern __IO uint32_t uwTick; 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, // 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 // and works when interrupts are disabled. This function is intended to be
// used only by the ST HAL functions. // used only by the ST HAL functions.

View File

@ -26,6 +26,22 @@
#ifndef MICROPY_INCLUDED_STM32_SYSTICK_H #ifndef MICROPY_INCLUDED_STM32_SYSTICK_H
#define 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); void systick_wait_at_least(uint32_t stc, uint32_t delay_ms);
bool systick_has_passed(uint32_t stc, uint32_t delay_ms); bool systick_has_passed(uint32_t stc, uint32_t delay_ms);