-
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
drivers: peripheral UART driver interface overhaul #3265
Conversation
#define UART_0_TX_PIN 6 | ||
#define UART_0_RX_PIN 7 | ||
#define UART_0_AF 8 | ||
#define UART_NUMOF (sizeof(uart_config) / sizeof(uart_conf_t)) |
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 suggest sizeof(uart_config) / sizeof(uart_config[0])
instead
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 particular reasons for this? I don't mind, just try to understand the benefits...
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.
Not a strong reason, but it doesn't depend on the type of uart_config
any more.
I like the changes in general. However, I don't understand the detailed usage for TX, how is uart_begin_tx and uart_write supposed to be used together? Is it possible to change the rx/tx callbacks after initializing the module? Will a default TX callback functionality be added to the write syscall handler for stdout? |
How should we handle Newlib may call a flush from inside printf which is expected to be blocking, if we don't block then buffered characters may be lost. In particular, it must be possible to print crash information in The printf from inside ISR has caused problems for me in the past. |
TX is indeed the slightly more advanced part of the UART driver. But I see no problem for implementing In the syscalls/uart_syscalls implementation, we define a ringbuffer for TX data. When ever
The TX callback should look more or less like this: int tx_cb(arg *arg)
{
if (tx_buf.avail > 0) {
char data = ringbuffer_get_one(&tx_buf);
uart_write(DEV, data);
return 1;
}
return 0;
There is no way at the moment, but neither do I really see a use case for this. But you can of course always call |
@haukepetersen What about the case of printf from inside ISR, how do we handle that? |
same, it does not make any difference if you call it from inside an ISR: all printf does is basically filling a ringbuffer and setting the uart TX isr interrupt. All we need to make sure, that we disable interrupts whenever tinkering the the TX buffer, so that there is no concurrent write access to the buffer... |
@haukepetersen What happens if the buffer gets full? Does the printf block? if so, we will get a deadlock if the blocking printf was called from inside an ISR. |
nope, I would not block, Id say we just throw away anything that does not fit into the buffer. Also we could implement both: block, but only if not in interrupt context. This is just a question of how we implement the syscalls, this is completely independent from the UART driver... |
I did a check for PRIMASK in #2605 in blocking mode, maybe some similar solution may be appropriate here? |
PRIMASK would not work here, as it is not portable. We already have the |
Right, I was confused by the name, since IRQs may be disabled outside of
|
Sure, we could rename it (and do also s/enableIRQ()/irq_enable()/ while we are at it), but that's some other issue... |
PR needs rebase |
e061133
to
bca95b5
Compare
rebased. |
This (ii) is unfortunately not correct. Our newlib _write() implementation only used blocking mode, and many platforms only implemented that. So essentially, only blocking mode was ever used. I'm with @gebart here, we need to find a rock-solid concept for ISR and crash cases here, otherwise we'll cause more trouble than the performance gain can justify. |
Almost, the |
I agree a 100%! |
but the `uart_read_blocking()` was not used, and that one I threw out of the interface...
Ah ok, perfect!
|
bca95b5
to
09ab627
Compare
3e1ddc6
to
57cbfb4
Compare
After some more test and discussions offline, I updated the UART interface once more. I decided for a different concept when sending data. The main reasons for this are:
Now there is only once send function A test implementation is done for the @daniel-k, @PeterKietzmann, @gebart, @kaspar030: What to you think about this change? If you approve, I will finally go through all the CPUs again and adapt them. |
I haven't looked at any implementation yet, just at |
No, I actually menat 'once the buffer is send'. The idea is, that the function really does block for the time it takes to send the data and only returns after the bytes have been send out. This does however not implay, that the thread is actively blocking, as it is doing when using the current Making the send function synchronous as this reduces the implementation complexity by some factors and removes on many occasions the need for double buffering, hence making the use of UART more memory efficient. |
I see your point. The use case I had recently was that I had a lot debug output in some relatively timing sensitive code that could only be debugged reasonably with non-blocking UART. I'm not sure if there are others. |
I like it a lot. The interface is probably as simple as possible now. |
Also I like the proposal in general, I think @daniel-k has a very valid point here. (And to be a smartass, the documentation should be |
We should introduce caching, but not at this level. |
@daniel-k I see your use-case. But in general, I don't see
Yes! This was always the idea with this interface. |
@haukepetersen In my case the code breaks when the debugging introduces too big delays, but I can't just debug with 2 GPIOs. A trace tool would be the right choice. |
And not everyone has everywhere access to logic analyzers. Think for, e.g. testbeds. |
The issue is not whether we want fast uart output, but if it should become asynchronous at this level (within the uart interface). |
Yes, I agree with this. |
So, basically we need an accompanying PR that introduces this caching somewhere above, right? |
@OlegHahm Wasn't all uart output synchronous before? |
Was it? I dunno. If this is true, yes, we can work on this later on. |
I think @kaspar030 is right. But @haukepetersen was introducing interrupt-driven UART with this PR that got me excited and now you take that toy away again :-D |
YES.
Not quite. Interrupt driven UART did exist before... And to clarify - I am not taking it away again, the decision of how to use the UART is simply moved from the user of the interface into the implementation of the interface. |
For UARTs with integrated FIFO buffers, I assume it's ok to just pass the data to it and return? (and check on the next call, if the FIFO buffer can be written again?) |
Yes. |
closed in favor of #4114 |
As already merged for GPIO and PR's for SPI (#3216), here is a slight overhaul to the peripheral UART driver. The most notable changes:
uart_init_blocking()
anduart_init()
into one function, dropping the blocking modeuart_receive_blocking()
functionvoid xxx(...)
The main reasons for removing the blocking mode are: (i) the blocking mode is in-efficient and I believe we should not push it's usage by providing this interface and (ii) it was not used anywhere...
For discussion I would like to focus on the changes to the UART interface first. Once we agree on them, I will adjust all the boards/CPUs if neccessary.