Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HardwareSerial/Uart write and flush lock up when interrupts are disabled #472

Open
matthijskooijman opened this issue Nov 28, 2019 · 1 comment

Comments

@matthijskooijman
Copy link
Collaborator

In Uart (HardwareSerial), when the write() method is called and the buffer is full, it blocks until buffer room becomes available. When flush() is called, it blocks until the buffer is empty (and the last byte is also transmitted).

However, when these methods are called with interrupts disabled (or from an ISR with higher priority than the UART interrupt), the ISR that makes room in the buffer will never run, so this waiting blocks indefinitely, locking up the system.

Of course, one can say that serial printing from an ISR or with interrupts disabled is not a good idea, but it often makes sense for debugging (or e.g. for printing an error on a failed assertion).

On AVR, this is fixed by detecting that interrupts are disabled, and if so, manually checking the interrupt flag and run the ISR manually when the interrupt flag is set (code). This makes sure that printing and flushing with interrupts disabled works as expected (there is still the related issue of Uart code that can get interrupted by an ISR that accesses the UART, leading to inconsistent state, but that is a separate issue and far less likely to occur in practice, so not so problematic for debugging).

It would be good to implement the same thing in the SAMD core Uart. One complication is that while on AVR there is a single "interrupts are disabled" flag, on SAMD you would have to check:

  • Whether an exception is currently running, and if its priority is higher than the UART priority (Can be done using this method, noting that the M0+ has no VECTACTIVE, but does have the IPSR register that provides the needed information).
  • Whether the PM bit in the PRIMASK register is set (which disables all interrupts)

See also http://infocenter.arm.com/help/topic/com.arm.doc.dui0662b/DUI0662B_cortex_m0p_r0p1_dgug.pdf

An implementation could look like (pseudocode):

while (wait_for_buffer_space) {
  if (IPSR && GetPriority(IPSR) <= GetPriority(irqn) || (PRIMASK & PM)
	run_isr();
}

This might also need to clear the pending bit if that is not automatic. Also, the if might be good to put into a utility function, so it can be used in other places too.

One alternative implementation would be to detect this situation and temporarily raise the Uart interrupt priority until the loop completes. However, this will not work when the PM bit is set, when the current interrupt already has priority 0 (or less, for e.g. NMI or hardfault), so this probably is not a viable option.

A completely different behaviour would be to detect that the ISR cannot run and drop bytes instead of waiting, but that significantly changes the behaviour of these functions (and this deviates from the AVR behaviour), so I don't think that's a good idea.

I suspect that the USB CDC code suffers from exactly the same problem and should probably be fixed in the same way, but have not tested this.

@matthijskooijman
Copy link
Collaborator Author

I think the same problem (and probably the same solution) applies to the delay() function as well (this is not fixed on AVR yet, though).

matthijskooijman added a commit to LacunaSpace/basicmac that referenced this issue Feb 12, 2020
On some Arduino cores, printing with IRQs disabled is problematic (see
e.g. arduino/ArduinoCore-samd#472). In
general, disabling interrupts for shorter amounts of time is a good idea
anyway.
matthijskooijman added a commit to LacunaSpace/basicmac that referenced this issue Feb 12, 2020
Serial printing with IRQs disabled is problematic on some Arduino cores,
see e.g.  arduino/ArduinoCore-samd#472
matthijskooijman added a commit to LacunaSpace/basicmac that referenced this issue Feb 12, 2020
This prevents printing with interrupts disabled, which is problematic on some
Arduino cores, see e.g.  arduino/ArduinoCore-samd#472
matthijskooijman added a commit to LacunaSpace/basicmac that referenced this issue Feb 12, 2020
On some Arduino cores, printing with IRQs disabled is problematic (see
e.g. arduino/ArduinoCore-samd#472). In
general, disabling interrupts for shorter amounts of time is a good idea
anyway.
matthijskooijman added a commit to LacunaSpace/basicmac that referenced this issue Feb 12, 2020
Serial printing with IRQs disabled is problematic on some Arduino cores,
see e.g.  arduino/ArduinoCore-samd#472
matthijskooijman added a commit to LacunaSpace/basicmac that referenced this issue Feb 12, 2020
This prevents printing with interrupts disabled, which is problematic on some
Arduino cores, see e.g.  arduino/ArduinoCore-samd#472
matthijskooijman added a commit to LacunaSpace/basicmac that referenced this issue Apr 15, 2020
On some Arduino cores, printing with IRQs disabled is problematic (see
e.g. arduino/ArduinoCore-samd#472). In
general, disabling interrupts for shorter amounts of time is a good idea
anyway.
matthijskooijman added a commit to LacunaSpace/basicmac that referenced this issue Apr 15, 2020
Serial printing with IRQs disabled is problematic on some Arduino cores,
see e.g.  arduino/ArduinoCore-samd#472
matthijskooijman added a commit to LacunaSpace/basicmac that referenced this issue Apr 15, 2020
This prevents printing with interrupts disabled, which is problematic on some
Arduino cores, see e.g.  arduino/ArduinoCore-samd#472
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant