litex: ensure we don't re-enable interrups during ISR

During an interrupt handler, interrupts are implicitly disabled. They
will be re-enabled when the interrupt handler returns.

Due to some changes that were made, varous calls will re-enable
interrupts after they're finished. Examples of this include calling
`CALLBACK_CRITICAL_END` and getting the number of ticks with
`port_get_raw_ticks()`.

This patch prevents this from happening by doing two things:

1. Use standard calls in `port_get_raw_ticks()` to disable and re-enable
interrupts, preventing nesting issues, and
2. Increase the nesting count inside `isr()`, reflecting the implicit
call that is made by hardware when an interrupt is handled

This helps to address #3841.

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2020-12-24 14:03:10 +08:00
parent 42ca57ff8f
commit f3e54414e5
2 changed files with 19 additions and 2 deletions

View File

@ -44,16 +44,31 @@ void mp_hal_delay_us(mp_uint_t delay) {
extern void SysTick_Handler(void); extern void SysTick_Handler(void);
// This value contains the number of times "common_hal_mcu_disable_interrupts()"
// has been called without calling "common_hal_mcu_enable_interrupts()". Since
// this is the interrupt handler, that means we're handling an interrupt, so
// this value should be `0`.
//
// Interrupts should already be disabled when this handler is running, which means
// this value is logically already `1`. If we didn't do this, then interrupts would
// be prematurely enabled by interrupt handlers that enable and disable interrupts.
extern volatile uint32_t nesting_count;
__attribute__((section(".ramtext"))) __attribute__((section(".ramtext")))
void isr(void) { void isr(void) {
uint8_t irqs = irq_pending() & irq_getmask(); uint8_t irqs = irq_pending() & irq_getmask();
// Increase the "nesting count". Note: This should be going from 0 -> 1.
nesting_count += 1;
#ifdef CFG_TUSB_MCU #ifdef CFG_TUSB_MCU
if (irqs & (1 << USB_INTERRUPT)) if (irqs & (1 << USB_INTERRUPT))
usb_irq_handler(); usb_irq_handler();
#endif #endif
if (irqs & (1 << TIMER0_INTERRUPT)) if (irqs & (1 << TIMER0_INTERRUPT))
SysTick_Handler(); SysTick_Handler();
// Decrease the "nesting count". Note: This should be going from 1 -> 0.
nesting_count -= 1;
} }
mp_uint_t cpu_get_regs_and_sp(mp_uint_t *regs) { mp_uint_t cpu_get_regs_and_sp(mp_uint_t *regs) {

View File

@ -32,6 +32,8 @@
#include "irq.h" #include "irq.h"
#include "csr.h" #include "csr.h"
#include "shared-bindings/microcontroller/__init__.h"
// Global millisecond tick count. 1024 per second because most RTCs are clocked with 32.768khz // Global millisecond tick count. 1024 per second because most RTCs are clocked with 32.768khz
// crystals. // crystals.
volatile uint64_t raw_ticks = 0; volatile uint64_t raw_ticks = 0;
@ -129,9 +131,9 @@ uint32_t port_get_saved_word(void) {
uint64_t port_get_raw_ticks(uint8_t* subticks) { uint64_t port_get_raw_ticks(uint8_t* subticks) {
// Reading 64 bits may take two loads, so turn of interrupts while we do it. // Reading 64 bits may take two loads, so turn of interrupts while we do it.
irq_setie(false); common_hal_mcu_disable_interrupts();
uint64_t raw_tick_snapshot = raw_ticks; uint64_t raw_tick_snapshot = raw_ticks;
irq_setie(true); common_hal_mcu_enable_interrupts();
return raw_tick_snapshot; return raw_tick_snapshot;
} }