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

Core debug hardening #1826

Merged
merged 7 commits into from
Sep 26, 2022
Merged

Core debug hardening #1826

merged 7 commits into from
Sep 26, 2022

Conversation

fpistm
Copy link
Member

@fpistm fpistm commented Sep 8, 2022

Fixes #1789
Clean up the debug uart functions
Core debug is now half duplex by default if not linked to another Serial instance.
Tested:

  • Serial and debug on the same U(S)ART
  • Serial and debug on different U(S)ART

@fpistm fpistm added bug 🐛 Something isn't working enhancement New feature or request fix 🩹 Bug fix labels Sep 8, 2022
@fpistm fpistm added this to the 2.4.0 milestone Sep 8, 2022
@fpistm fpistm requested a review from ABOSTM September 8, 2022 15:30
@matthijskooijman
Copy link
Contributor

I'm not sure I understand the cause of the problem properly. Is it that the old code would just try to start transmission with the hAL until success or a timeout, but did this with interrupts enabled, so the uart would (if Serial transmission was ongoing) would never change to READY state (because no ISR would run) and it would always fail?

If so, I can imagine that another fix could have been to simply remove the disable/enable IRQ lines? Though just waiting for idle and then trying to transmit once is of course a lot cleaner.

I do wonder if the disable/enable IRQ lines are still needed with the current code? AFAICS that once the uart is READY, the IRQ will be disabled anyway (not the complete IRQ, but all IE flags)? So the only thing that this would protect against is if the UART IRQ itself would start another transmission, which seems impossible (and if not, the code would still have a race condition to fix). Or is this intended to guard against something in the RX handling that makes the UART non-READY again? Speaking of which - I think that the current code might actually break RX by keeping the IRQ disabled, so that's one more reason to remove the IRQ disabling (though I guess since debug transmits one byte at a time, IRQs are never disabled long enough to really break reception).

To be clear, I'm talking about this section:

HAL_NVIC_DisableIRQ(serial_debug.irq);
if (HAL_UART_Transmit(huart, data, size, TX_TIMEOUT) != HAL_OK) {
size = 0;
}
HAL_NVIC_EnableIRQ(serial_debug.irq);

Note that disabling IRQs could still be useful to guard against a sketch that prints from an ISR, but that is not currently supported by HardwareSerial at all, I think (and also not by core_debug, since that would just timeout waiting for the uart to become READY).

One more thought: The use of a timeout waiting for READY also seems tricky - if called with interrupts disabled the UART never becomes READY so you always have to wait the full timeout (even though you could maybe already know that it would never happen - a bit tricky to know for sure maybe), but also the timeout might trigger when the Serial buffer is particularly full and takes a long time to empty (but at 1000ms timeout, that's probably only an issue on 300 or 600 baud or so, otherwise the HardwareSerial buffer is probably too small).

One more thought: This currently blocks until the entire HardwareSerial buffer is empty before transmitting (since if the current batch is completed, the TxComplete ISR calls into HardwareSerial which then immediately queues the next batch of pending data (if any)). It would maybe be nice if this would only wait for the current batch to complete, but I think this is essentially impossible, at least without changes to the HAL code (since the uart does not return to READY until the TxC ISR fires, and if that fires, it immediately queues up more).

Finally: I haven't tested the code yet. It looks like it would work at first glance, but I ran out of time for today. I'll test tomorrow :-)

since _write() syscall moved to Print.cpp

Signed-off-by: Frederic Pillon <[email protected]>
It prevents to check this each time uart_debug_write is called as
init is done once.

Signed-off-by: Frederic Pillon <[email protected]>
Since rts/cts has been introduced, the serial debug on
dedicated pins was broken as by default the serial_debug
pin cts/rts was set to 0 (valid pinname). So core_debug was
called during the init leading to an infinite loop.

Signed-off-by: Frederic Pillon <[email protected]>
@fpistm fpistm force-pushed the core_debug_review branch 2 times, most recently from 9c9ef29 to 6a5bc03 Compare September 22, 2022 09:00
@fpistm
Copy link
Member Author

fpistm commented Sep 22, 2022

Hi @matthijskooijman

After more investigation I've made a new proposal with no IRQ disable 😉 this allows to kept RX.
It is closed t the first proposal, I've only made some more cleanup using obj directly.

Copy link
Contributor

@matthijskooijman matthijskooijman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good. There is one worry I have still - if this code is called from inside an ISR (or another context where interrupts are disabled) and Serial has TX ongoing, I think this will result in an infinite loop, right? I know that logging/serial printing from inside an ISR is often considered unsupported and should not be done, but it is often very useful so if at all possible, we should support it (or at least not lock up - dropping output might be preferable then). For example AVR HardwareSerial does support it, and there's an issue about SAMD here (which also has some details on how it works on AVR).

libraries/SrcWrapper/src/stm32/uart.c Outdated Show resolved Hide resolved
@fpistm
Copy link
Member Author

fpistm commented Sep 22, 2022

if this code is called from inside an ISR (or another context where interrupts are disabled) and Serial has TX ongoing, I think this will result in an infinite loop, right?

Right. Note that to note block I've added the CORE_DEBUG_TIMEOUT

I know that logging/serial printing from inside an ISR is often considered unsupported and should not be done,

exactly :)

but it is often very useful so if at all possible, we should support it (or at least not lock up - dropping output might be preferable then). For example AVR HardwareSerial does support it, and there's an issue about SAMD here (which also has some details on how it works on AVR).

Propbably, anyway, I think it should be managed as a separate issue/PR.

@matthijskooijman
Copy link
Contributor

Propbably, anyway, I think it should be managed as a separate issue/PR.

Yeah, though the easy workaround could be to keep the timeout always active maybe? A proper fix would need more work to really flush the buffer instead of just doing a timeout, so that would definitely be for later.

I also think this might affect STM32LoRaWAN, since I think STM32CubeWL does some printing from inside ISRs too...

@fpistm
Copy link
Member Author

fpistm commented Sep 22, 2022

I also think this might affect STM32LoRaWAN, since I think STM32CubeWL does some printing from inside ISRs too...

Bad news. Will check what I can do.

Only TX pin is required.

Signed-off-by: Frederic Pillon <[email protected]>
Like state is ready not need to loop on the blocking
HAL_UART_Transmit(). If not ok it can be HAL_ERROR or HAL_TIMEOUT.
Moreover, it avoid to disable the U(S)ART IRQ which prevent to
receive data.

Fixes stm32duino#1789

Signed-off-by: Frederic Pillon <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something isn't working enhancement New feature or request fix 🩹 Bug fix
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Mixing core_debug and Serial printing results in slowdown and lost output
3 participants