Skip to content

Commit

Permalink
Add R/W memory and instruction barrier after mstatus access
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
KarlK90 committed Apr 16, 2021
1 parent fe3cdf8 commit b875108
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 13 deletions.
26 changes: 19 additions & 7 deletions os/common/ports/RISCV-ECLIC/chcore.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand All @@ -399,22 +401,32 @@ 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;
}

/**
* @brief Kernel-lock action.
* @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.
Expand All @@ -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.
Expand Down
20 changes: 14 additions & 6 deletions os/common/ports/RISCV-ECLIC/compilers/GCC/chcoreasm.S
Original file line number Diff line number Diff line change
Expand Up @@ -56,26 +56,34 @@

# Disable Interrupts globally.
.macro DISABLE_MIE
csrc CSR_MSTATUS, MSTATUS_MIE
csrc CSR_MSTATUS, MSTATUS_MIE
fence iorw, iorw
fence.i
.endm

# Enable Interrupts globally.
.macro ENABLE_MIE
csrs CSR_MSTATUS, MSTATUS_MIE
csrs CSR_MSTATUS, MSTATUS_MIE
fence iorw, iorw
fence.i
.endm

# Clear previous machine interrupt enable bit in mstatus (mstatus.mpie).
# On machine return (mret) mstatus.mie is assigned this value.
# Clearing this bit disables interrupts when leaving interrupt processing mode.
.macro DISABLE_MPIE
li a0, MSTATUS_MPIE
csrc CSR_MSTATUS, a0
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
li a0, MSTATUS_MPIE
csrs CSR_MSTATUS, a0
fence iorw, iorw
fence.i
.endm

# --------------------------------------------------------------------------
Expand Down

0 comments on commit b875108

Please sign in to comment.