Add R/W memory and instruction barrier after mstatus access

Fast subsequent reads and writes to the mstatus csr lead to
illegal instruction exceptions on the nucleisys bumblee core
of the gd32vf103. This behavior only occurred in high load
situations e.g. interrupt frequency of 5khz but reliably let
to these errors.  Adding the instruction and memory barriers solved
the problem. There is some negligible performance impact.
This commit is contained in:
Stefan Kerkmann 2021-04-16 16:07:11 +02:00
parent fe3cdf8314
commit b875108cd0
2 changed files with 33 additions and 13 deletions

View File

@ -377,7 +377,9 @@ static inline void port_init(void) {}
* @return The interrupts status.
*/
static inline syssts_t port_get_irq_status(void) {
return (syssts_t)__RV_CSR_READ(CSR_MSTATUS);
syssts_t mstatus = __RV_CSR_READ(CSR_MSTATUS);
__RWMB();
return mstatus;
}
/**
@ -399,7 +401,9 @@ static inline bool port_irq_enabled(syssts_t sts) { return sts & MSTATUS_MIE; }
* @retval true running in ISR mode.
*/
static inline bool port_is_isr_context(void) {
return __RV_CSR_READ(CSR_MSUBM) & MSUBM_TYP;
bool is_irq_context = (__RV_CSR_READ(CSR_MSUBM) & MSUBM_TYP) != 0;
__RWMB();
return is_irq_context;
}
/**
@ -407,14 +411,22 @@ static inline bool port_is_isr_context(void) {
* @details Usually this function just disables interrupts but may perform more
* actions.
*/
static inline void port_lock(void) { __disable_irq(); }
static inline void port_lock(void) {
__disable_irq();
__RWMB();
__FENCE_I();
}
/**
* @brief Kernel-unlock action.
* @details Usually this function just enables interrupts but may perform more
* actions.
*/
static inline void port_unlock(void) { __enable_irq(); }
static inline void port_unlock(void) {
__enable_irq();
__RWMB();
__FENCE_I();
}
/**
* @brief Kernel-lock action from an interrupt handler.
@ -436,18 +448,18 @@ static inline void port_unlock_from_isr(void) { port_unlock(); }
* @brief Disables all the interrupt sources.
* @note Of course non-maskable interrupt sources are not included.
*/
static inline void port_disable(void) { __disable_irq(); }
static inline void port_disable(void) { port_lock(); }
/**
* @brief Disables the interrupt sources below kernel-level priority.
* @note Interrupt sources above kernel level remains enabled.
*/
static inline void port_suspend(void) { __disable_irq(); }
static inline void port_suspend(void) { port_lock(); }
/**
* @brief Enables all the interrupt sources.
*/
static inline void port_enable(void) { __enable_irq(); }
static inline void port_enable(void) { port_unlock(); }
/**
* @details The function is meant to return when an interrupt becomes pending.

View File

@ -57,11 +57,15 @@
# Disable Interrupts globally.
.macro DISABLE_MIE
csrc CSR_MSTATUS, MSTATUS_MIE
fence iorw, iorw
fence.i
.endm
# Enable Interrupts globally.
.macro ENABLE_MIE
csrs CSR_MSTATUS, MSTATUS_MIE
fence iorw, iorw
fence.i
.endm
# Clear previous machine interrupt enable bit in mstatus (mstatus.mpie).
@ -70,12 +74,16 @@
.macro DISABLE_MPIE
li a0, MSTATUS_MPIE
csrc CSR_MSTATUS, a0
fence iorw, iorw
fence.i
.endm
# Set previous machine interrupt enable bit in mstatus (mstatus.mpie).
.macro ENABLE_MPIE
li a0, MSTATUS_MPIE
csrs CSR_MSTATUS, a0
fence iorw, iorw
fence.i
.endm
# --------------------------------------------------------------------------