-
Notifications
You must be signed in to change notification settings - Fork 2k
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
cpu/sam0_common/periph/uart: implement non-blocking write #12064
Conversation
I experimented with this a while ago, but never PR'd it. Here is my branch: I defined a new function uart_write_async which blocks only if there is data waiting to be sent, otherwise it queues the current data and returns. I used a zero-copy approach by just storing a pointer to the buffer and then I redefined uart_write in terms of uart_write_async. |
I don't think zero-copy is the right approach for a general |
Isn't it better to create a dedicated pseudomodule for this ? the ROM cost looks heavy otherwise. |
Is there any application that stops fitting because of this? The cost of making it optional (at leat in the way it is done here and in most of riot) is a considerable increase in complexity and a decrease in readability, with all the ifdefs. Add to that the fact that now you have two code paths in the uart driver that you have to test. |
Is this a normal (allowed?) way of defining a regular macro?
In most cases when I see Shouldn't we just have a macro (defined through CFLAGS) with a name such as |
Notice that there is something called |
There is already PSEUDOMODULES += periph_% in |
@benpicco Yes, you're right. So it's not a problem. |
8f61d99
to
fb96555
Compare
I took the liberty to squash because it's more convenient being able to just grab the patch when debugging race conditions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any reason to keep SAM D&L 21 away from these changes ?
Makefile.dep
Outdated
ifneq (,$(filter periph_uart_nonblocking, $(USEMODULE))) | ||
USEMODULE += periph_uart | ||
endif | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So everything that depends on periph_uart
is also fulfilled with periph_uart_nonblocking
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But should it belongs to Makefile.dep ? Why not cpu/sam0_common/Makefile.dep ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So other platforms can implement it too.
I also tested this on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've looked quickly at this PR. I have no comment on the implementation of the non blocking uart mode in sam0 (I'm not an expert on these platforms) but I think the integration in the build system needs a complete rework:
- add
FEATURES_PROVIDED += periph_uart_nonblocking
tocpu/sam0_common/Makefile.features
- in
Makefile.dep
, just add:instead of the current version (See how this is already done forifneq (,$(filter periph_uart_nonblocking,$(USEMODULE))) FEATURES_REQUIRED += periph_uart endif
periph_gpio
/periph_gpio_irq
inMakefile.dep
) - Remove all uses and definition of the
USE_NONBLOCKING
macro and just replace the uses of#if USE_NONBLOCKING
by#ifdef MODULE_PERIPH_UART_NONBLOCKING
- Remove all
cpu/samxx/Makefile.dep
introduced by this PR: they are empty and thus useless. - add a test application with:
in its makefile. This application will print some data and this should work. A Python test script could be added for this purpose.
FEATURES_REQUIRED += periph_uart periph_uart_nonblocking
Note that, thanks to the useFEATURES_REQUIRED
, this application will only be built for sam0 based boards as only sam0 provides this feature (for the moment). The good thing it that it will ensure that the CI is building the nonblocking related code.
I'm blocking this PR until the mentioned problems are fixed and a dedicated test application is added.
They are all but useless. This implementation depends on I've addressed all the other comments.
with
with |
You are right. I don't know why I thought they were initially empty. Thanks for updating following the other suggestions. Just one is still missing: add a Python script for automatic testing. |
a5bfd13
to
ff4c18b
Compare
I added a small Python script. |
@dylad, can you have a look at this one when you have time ? I'm fine with the test application and automatic test script. The build system integration is also ok for me. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SAML21 isn't happy. See comment below.
Can someone doublecheck on SAMD21 just to be sure ?
NVIC_EnableIRQ(SERCOM0_2_IRQn + (sercom_id(dev(uart)) * 4)); | ||
#else | ||
/* enable UART ISR */ | ||
NVIC_EnableIRQ(SERCOM0_IRQn + sercom_id(dev(uart))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got a hard time debugging this one.
In the current shape the provided test app doesn't work at all on SAML21.
No DRE interrupt is triggered. In fact, the NVIC_EnableIRQ is never call because there is no callback provided so we did not enter here.
The callback is not provided because weren't using a shell.
So we need a second way to enable the NVIC in this specific case (TX only, no RX).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea you are right.
I only tested the non-callback case on samd5x, for samd2x/saml2x the UART interrupt was only enabled when a callback was provided.
It is now also enabled when MODULE_PERIPH_UART_NONBLOCKING
is defined.
cpu/sam0_common/periph/uart.c
Outdated
NVIC_EnableIRQ(SERCOM0_0_IRQn + (sercom_id(dev(uart)) * 4)); | ||
#else | ||
/* enable UART ISR */ | ||
NVIC_EnableIRQ(SERCOM0_IRQn + sercom_id(dev(uart))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In some case, the NVIC_EnabeIRQ is called twice, this might be confusing.
I would rather go to something like :
if ((rx_cb) && (uart_config[uart].rx_pin != GPIO_UNDEF)) {
uart_ctx[uart].rx_cb = rx_cb;
uart_ctx[uart].arg = arg;
#ifdef UART_HAS_TX_ISR
/* enable RXNE ISR */
NVIC_EnableIRQ(SERCOM0_2_IRQn + (sercom_id(dev(uart)) * 4));
#else
/* enable UART ISR */
NVIC_EnableIRQ(SERCOM0_IRQn + sercom_id(dev(uart)));
#endif /* UART_HAS_TX_ISR */
dev(uart)->CTRLB.reg |= SERCOM_USART_CTRLB_RXEN;
dev(uart)->INTENSET.reg |= SERCOM_USART_INTENSET_RXC;
/* set wakeup receive from sleep if enabled */
if (uart_config[uart].flags & UART_FLAG_WAKEUP) {
dev(uart)->CTRLB.reg |= SERCOM_USART_CTRLB_SFDE;
}
}
#if defined(MODULE_PERIPH_UART_NONBLOCKING)
else {
NVIC_EnableIRQ(SERCOM0_IRQn + sercom_id(dev(uart)));
}
#if defined(UART_HAS_TX_ISR)
/* enable TXE ISR */
NVIC_EnableIRQ(SERCOM0_0_IRQn + (sercom_id(dev(uart)) * 4));
#endif
#endif /* MODULE_PERIPH_UART_NONBLOCKING */
This now works on SAML21 but I tested on SAML10 and SAML11 and here is result WITH uart_nonblocking:
The output is fine without the uart_nonblocking module. I'll try to investigate further. |
One really quick&dirty workaround is the following:
But I'd like to find a better patch... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested work on SAMR21 (Iotlab), SAML10, SAML11, SAML21, SAME54.
Please squash !
Implement interrupt based uart_write() using a tsrb for the TX buffer. To enable it, add USEMODULE += periph_uart_nonblocking to your Makefile.
9fdebe8
to
8e35c24
Compare
static tests failed, you can amend directly |
The application is mainly to compile-test non-blocking UART functionality, but some functional testing is also possible. With non-blocking UART the total runtime of the program is 2100735 µs on same54-xpro. With blocking UART the total runtime is 2152407 µs.
8e35c24
to
0c08abd
Compare
Here we go ! Thanks for the PR @benpicco ! |
Thank you for the review! |
Contribution description
This replaces the polling based UART TX implementation on the sam0 platform with an interrupt based one.
Thread-safe Ringbuffer is used for the TX buffer with a compile-time configurable buffer size. (May be tweaked.)
This violates the specification ofuart_write()
as this makesuart_write()
non-blocking.The non-blocking mode is now only used if the module
periph_uart_nonblocking
is used.I wanted to have a non-blocking debug output when debugging a race condition, so I implemented this. Maybe a non-blocking uart_write could be generally useful though.
Testing procedure
add
USEMODULE += periph_uart_nonblocking
to your application.Everything should behave as before, although the console output feels more snappy.
Only the shell / debug output have been tested.
Issues/PRs references
none