-
Notifications
You must be signed in to change notification settings - Fork 6.9k
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: serial: nrfx_uarte: Refactoring poll_out #28961
drivers: serial: nrfx_uarte: Refactoring poll_out #28961
Conversation
c888b9f
to
74fac23
Compare
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 only gone through the first commit, the main one will take more time.
Has it always been understood that it's safe to use a serial device with the polled API when it's also configured for one of the interrupt-based APIs? I think that's what this provides....
status = "okay"; | ||
tx-pin = <6>; | ||
rx-pin = <8>; | ||
rts-pin = <0>; |
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.
Is this right? Deleting the property is supposed to be the way to mark that it's not supported. This would seem to assign the XL1 pin to be both RTS and CTS.
By inspection using -1
as the pin ordinal should also have the desired effect. 0xffffffff
(the value of NRF_UART_PSEL_DISCONNECTED
should also.
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.
yeah, it's a copy paste from test uart_async_api
. Will clean it up.
@@ -0,0 +1,56 @@ | |||
tests: |
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.
This should probably have a README explaining how it's supposed to work. Does it not require an external loopback connection to short RX/TX CTS/RTS?
poll_out is used by printk from any context and at the same type by the logger/shell. Shell works with interrupt driven api. So uart was requested from multiple contexts by both api's. |
drivers/serial/uart_nrfx_uarte.c
Outdated
} | ||
|
||
tx_start(uarte, buf, len); | ||
|
||
out: |
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.
} | |
tx_start(uarte, buf, len); | |
out: | |
} else { | |
tx_start(uarte, buf, len); | |
} |
drivers/serial/uart_nrfx_uarte.c
Outdated
@@ -511,14 +577,23 @@ static int uarte_nrfx_tx(const struct device *dev, const uint8_t *buf, | |||
} | |||
|
|||
data->async->tx_buf = buf; |
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 think it can be moved right before tx_start at line 591
drivers/serial/uart_nrfx_uarte.c
Outdated
|
||
tx_start(uarte, data->int_driven->tx_buffer, len); | ||
|
||
out: |
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.
Also can be replaced by else
static bool enable = true; | ||
static uint8_t async_rx_buf[4]; | ||
|
||
if (async & !async_rx_enabled) { |
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.
Probably boolean operator instead of bitwise
74fac23
to
5363dfe
Compare
@Mierunski thanks for review, comments applied. |
5363dfe
to
c25e267
Compare
drivers/serial/uart_nrfx_uarte.c
Outdated
if (--safety_cnt == 0) { | ||
LOG_ERR("Dropping byte in poll_out"); | ||
return; |
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.
With this still present, this PR does not really fix #26722. As stated there:
The expected behavior for hardware flow control used with uarte_nrfx_poll_out() is that it would block forever until the other side asserts CTS.
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.
removed any timeout when hwfc is enable, in thread mode also removed a timeout since byte should eventually be sent (unless CTS is asserted)
c25e267
to
6646502
Compare
drivers/serial/uart_nrfx_uarte.c
Outdated
if (data->async) { | ||
uarte_nrfx_isr_async(dev); | ||
} |
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.
Is that still needed, actually, now that the loop waits for certain hardware event?
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.
we need to handle potential completion of tx transfer if we are in higher than UART irq priority context. During this completion AMOUNT register is read so it cannot be done after poll out transfer.
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.
Okay, I see. Non-HW byte counting would also benefit from those calls in such scenario.
But when it comes to the AMOUNT register readout, it is done in txstopped_isr()
, so we'll have a race condition here, as the TXSTOPPED event can occur between the calls to uarte_nrfx_isr_async()
and is_tx_ready()
.
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.
yes, noticed that yesterday and working on solution.
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.
TXSTOPPED event can occur between the calls to uarte_nrfx_isr_async() and is_tx_ready().
it's not only this, if we are in lower priority interrupt then interrupt will preempt at any moment within uarte_nrfx_isr_async
and the same other way round. Basically, we cannot have uarte_nrfx_isr_async
there.
drivers/serial/uart_nrfx_uarte.c
Outdated
NRFX_WAIT_FOR(is_tx_ready(dev), | ||
POLL_OUT_TIMEOUT_US, 1, res); |
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 do we need this timeout? Is this possible that the event won't eventually appear in a scenario with the flow control disabled?
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.
will remove
if (nrf_uarte_int_enable_check(uarte, NRF_UARTE_INT_ENDTX_MASK) && | ||
nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDTX)) { | ||
endtx_isr(dev); |
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.
This should be done only if (!get_dev_config(dev)->ppi_endtx)
. Also in uarte_nrfx_isr_async()
, so perhaps it would be then good to move all those checks to endtx_isr
?
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 when ppi is used then ENDTX interrupt is never enabled so nrf_uarte_int_enable_check(uarte, NRF_UARTE_INT_ENDTX_MASK)
is equivalent to !get_dev_config(dev)->ppi_endtx
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.
Right, I missed that.
drivers/serial/uart_nrfx_uarte.c
Outdated
if (nrf_uarte_int_enable_check(uarte, NRF_UARTE_INT_ENDTX_MASK) && | ||
nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDTX)) { | ||
endtx_isr(dev); | ||
return; |
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.
This return
should be removed. The handler should exit here only when interrupt driven API is not used for the instance, but this case is handled in line 221.
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.
done
6646502
to
c61a0fe
Compare
I've removed |
drivers/serial/uart_nrfx_uarte.c
Outdated
@@ -62,7 +69,9 @@ struct uarte_async_cb { | |||
* transmission by uart_poll_out | |||
*/ | |||
const uint8_t *volatile tx_buf; |
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.
This no longer needs to be volatile
. Also the comment above became outdated.
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.
done
#endif | ||
lock = &data->poll_out_lock; |
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.
poll_out_lock
should be also removed from the uarte_nrfx_data
structure.
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.
done
size_t amount = (data->async->tx_amount >= 0) ? | ||
data->async->tx_amount : nrf_uarte_tx_amount_get(uarte); |
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.
You need to lock interrupts for these operations, as uarte_nrfx_poll_out()
might potentially get called in between.
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.
good point, done.
drivers/serial/uart_nrfx_uarte.c
Outdated
@@ -80,6 +89,7 @@ struct uarte_async_cb { | |||
gppi_channel_t ppi; | |||
uint32_t cnt; | |||
} rx_cnt; | |||
int tx_amount; |
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 should be volatile
because it is read in the loop in uarte_nrfx_poll_out()
.
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.
done
7902cfd
to
d0e2463
Compare
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.
LGTM now.
|
||
static void process_byte(uint8_t b) | ||
{ | ||
int base = b/16; |
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.
b >> 4
would be more meaningful in this context.
ok = ((b - src->prev) == 1) || (!b && (src->prev == 0x0F)); | ||
|
||
zassert_true(ok, "Unexpected byte received %x, prev:%x", | ||
base | b, base | src->prev); |
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.
Looks like it should be (base << 4)
in both cases.
|
||
ok = ((b - src->prev) == 1) || (!b && (src->prev == 0x0F)); | ||
|
||
zassert_true(ok, "Unexpected byte received %x, prev:%x", |
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.
zassert_true(ok, "Unexpected byte received %x, prev:%x", | |
zassert_true(ok, "Unexpected byte received 0x%02x, prev: 0x%02x", |
Added test which is calling uart_poll_out from various contexts and asynchronous/interrupt driven API. Test is validating that calls can be preempted at any moment and no data is lost. Signed-off-by: Krzysztof Chruscinski <[email protected]>
Refactoring poll_out to be ready for handling preemption. uart_tx and uart_fifo_fill modified so they are resilient to being preempted by uart_poll_out. Refactored uart_poll_out implementation to be common for interrupt driven API and asynchronous API. In both APIs active state is detected by evaluating state of ENDTX and TXSTOPPED events. If anyone is set it means that new transfer can be started. Patch is fixing existing issues: - potential bytes dropping with flow control enabled - busywaiting for ENDTX (asynchronous API) - poor performance - potential bytes dropping during preemption - potential uart_tx returning -EBUSY when interrupted poll_out Signed-off-by: Krzysztof Chruscinski <[email protected]>
Update nordic boards to use UARTE driver for UART0 by default. Signed-off-by: Krzysztof Chruscinski <[email protected]>
d0e2463
to
72808d9
Compare
@anangl thanks for review, your final comments applied. |
@carlescufi can you merge this? |
PR contains refactoring of poll_out and test which validates that
uart_poll_out
,uart_tx
anduart_fifo_fill
are resilient to preemptions.Patch is fixing existing issues:
- potential bytes dropping with flow control enabled (#26722)
- busywaiting for ENDTX (asynchronous API) - poor performance
- potential bytes dropping during preemption
- potential uart_tx returning -EBUSY when interrupted poll_out
In general, poll_out implementation is now common for all APIs and relies on the fact that ENDTX and TXSTOPPED events are cleared atomically only before starting new transfer so new poll out can be started if TXSTOPPED (or ENDTX) is set.
Added option (enabled by default) to use PPI to connect ENDTX with STOPTX. This way there is no need for interrupt on ENDTX to stop TX but at the cost of 1 ppi channel (thus option to disable it).
Test is doing following:
uart_tx
/uart_irq_tx_enable
called from another threadFrom each context data is sent. Data increments from 0 to 0xf and has context_id on 4 MSB bits. This way receiver is able to detect if any byte is losed from any context.
Additionally, enabled
nordic-nrf,uarte
as default compatible for uart0 on all nordic boards since UARTE driver can be considered "better" than UART after this change.Fixes #26722.