-
Notifications
You must be signed in to change notification settings - Fork 1.2k
STM32: USART: Add eager_reads
config option
#4668
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
Conversation
cc85eb4
to
224ebef
Compare
I'm no expert here but the code looks correct to me (it matches the description of the intended changes). The one concern I have is that Related: I really wish there was a way to 'push down' the "desired number of bytes" from the higher-level read operations, so that the UART drivers could return when that number of bytes have been read or idle is detected. |
On the behaviour change: we discussed this a bit on matrix; I don't think it's technically a breaking change as the previous behaviour wasn't documented to be early-return or otherwise and either way you still get bytes through. More to the point I can't think of a way to have the config change for each type (and I'm not sure we would want to either!). For asking for a certain number of bytes, I think we could fairly easily extend both Buffered and RingBuffered to do that in a later PR since this PR adds all the infrastructure to return after a certain number of bytes are found, but I'm not sure what the use-case is - if the remote end transmits continuously you presumably want to fill/half-fill the buffer, and if it has gaps between messages the idle detection should pick it up? |
That's fair, although it's likely to end up being a Hyrum's Law-type breaking change instead of a documented breaking change. Anyone who has been expecting the future to be completed when the first byte arrives, because that's how it has been behaving, will be surprised when that no longer happens.
The protocol between my host and device has packets as small as 10 bytes and as large as 2060 bytes (approximately). This means the buffer has to be large enough for the largest possible packet, so waiting for the 50%/100% lines to be crossed will induce substantial latency when the host sends 3 or 4 10-20 byte packets in a row. Since I'd like to be able to respond to a packet as soon as it has arrived and not have to wait for the line to go idle, I'd prefer for the future to complete as soon as the minimum number of bytes for a packet have arrived - if those bytes are not a complete packet, then I'll wait for more (and I'll know how many to wait for as well). |
Ah, good reasoning for wanting to just get a few bytes then. I realise I have some similar use cases, where e.g. you might know the first 6 bytes give you a header with a length, and then you could do a second read for the rest of the packet all quite efficiently. Does |
I make use of COBS which uses a NUL-byte as frame-delimiter. Uart supports sigle-byte/address matching, with can generate a interrupt when the nul-byte is detected. Accourding to the RM0481, it is called Love to see that this also works with both |
In the G0 series 'address mark detection' is quite limited and only available when using |
Originally I thought it might, but after looking through the driver code I don't think it would work because I don't think it can tolerate the line going idle (if the sender stops transmitting prematurely). I haven't actually tested that though. |
OK, I've tested this and it behaves as expected. Ready for review/merge I think. I've added two new commits though:
I think it would be neat to extend this to support also stopping at arbitrary buffer sizes as @kpfleming said - BufferedUartRx could very easily do it with an extra check in the interrupt handle instead/alongside half_full, while RingBufferedUartRx would have to enable the RXNE interrupt to allow it to check (as it does now for Similarly I think a character detect interrupt could be easily supported by adding a config option, setting ADD and enable CMIE, and then detecting CM in the interrupt handler and waking the Rx waker. It looks like for some families at least this works outside of MUTE mode (e.g. H7). |
91bd04f
to
aa8adee
Compare
You're right, it would hang waiting for bytes that won't arrive instead of detecting idle. I think this is better as a config option alongside/instead of |
1ba5580
to
785d526
Compare
I updated the new |
914110b
to
1876f5a
Compare
Rebased to main. |
1876f5a
to
27cbbae
Compare
Rebased to main again. Any chance of getting this reviewed/merged? |
…y wrap around the internal buffer end
27cbbae
to
7751234
Compare
/// | ||
/// Has no effect on plain `Uart` or `UartRx` reads, which are specified to either | ||
/// return a single word, a full buffer, or after line idle. | ||
pub eager_reads: Option<usize>, |
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.
If "Some(0)
is treated as None
.", wouldn't this be better as NonZeroUsize?
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.
It could be Option<NonZeroUsize>
, which would make sense but is much more annoying to actually use and I don't think gives any meaningful benefit here.
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.
Ah okay I thought this is state, not configuration. I guess that makes you right :)
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.
🚀
Add a new option to control the behaviour of
BufferedUartRx
andRingBufferedUartRx
.Currently,
BufferedUartRx
returns as soon as any data is available in the buffer, even just a single byte, whileRingBufferedUartRx
returns either when line idle is detected or when the DMA internal buffer half-full or full position is written over.In my case it's useful to have
BufferedUartRx
wait for more data so I can process larger packets (and typically complete packets due to the line idle), but in other cases it's useful forRingBufferedUartRx
to return eagerly to reduce latency. This change allows choosing either behaviour and makes the two types consistent.For
BufferedUartRx
the modification is fairly straightforward;RingBuffer
gainsis_half_full()
, the interrupt handler wakes the waker if either "eager and buffer not empty" or "not eager and buffer half full" or "line idle". The RXNE interrupt is always enabled anyway.For
RingBufferedUartRx
it's a bit trickier. We didn't use RXNE at all previously so it's now conditionally enabled when eager is set. The interrupt handler can't observe the RXNE bit since the DMA has already cleared it, so it always wakes the waker. The waker previously checked if IDLE was set and returnedNotReady
otherwise but now we'd like it to check if the DMA buffer has received any data (indicating the interrupt was due to a received word). However, the second future is waiting for DMA half/full completion, so holds a borrow on the DMA buffer. Instead, we always returnReady
from the UART future whenever the interrupt fires andeager
is configured, and then afterwards check if the DMA buffer contains any data and recreate the futures if not.Currently this isn't tested but I think it's feature-complete, I plan to do some testing in the next couple of days.
CC @kpfleming @DrTobe