-
Notifications
You must be signed in to change notification settings - Fork 6.5k
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: Rework driver to support new features #78887
base: main
Are you sure you want to change the base?
drivers: serial: nrfx_uarte: Rework driver to support new features #78887
Conversation
c140ae3
to
2bc614a
Compare
6279c82
to
863a6e2
Compare
…struct pointer Refactor RX asynchronous API function to use a pointer to the RX async data structure instead of top level data structure pointer. It improves readability with more concise code. Upstream PR: zephyrproject-rtos/zephyr#78887 Signed-off-by: Krzysztof Chruściński <[email protected]>
…new features Rework driver to support new way of asynchronous RX handling. Previously RX was handled in two modes: using RXDRDY interrupt for byte counting or TIMER + PPI. Both modes had flaws. RXDRDY interrupt mode could miscalculated amount of received bytes when interrupt was not handled on time. Data was not lost but was not reported on time that could lead to issues. PPI+TIMER mode requires additional resources thus it was not the default mode. Often user was not aware of that option and was expiriencing driver RX faults. New RX mode is switching buffers when there is new data (RXDRDY event not set for given amount of time). It does not require additional resources to get precise byte counting. Additionally, this is in line with new UARTE feature (RX frame timeout) which is present in nRF54X devices. The behavior of the driver is the same for legacy devices and new one. For legacy devices k_timer periodic interrupts are used to check if there are any new bytes and it is not needed when RX frame timeout is present. Improved RX mode is enabled by default (CONFIG_UART_NRFX_UARTE_ENHANCED_RX=y) but legacy modes are still available though not recommended to be used. Note that new RX mode behaves a bit different because timeout always triggers switch of buffers which means that there will be no UART_RX_RDY events with non-zero offset. It also means that every UART_RX_RDY will be followed by UART_RX_BUF_RELEASED. After rework, driver is recommended to be used for all platforms as it performs much better and takes much less code than the second UART shim available for Nordic devices. Upstream PR: zephyrproject-rtos/zephyr#78887 Signed-off-by: Krzysztof Chruściński <[email protected]>
…eout Frame timeout is a hardware feature present in newer versions of UARTE (e.g. in NRF54X platforms) for detecting idle state on RX line and ending RX after configurable timeout. Upstream PR: zephyrproject-rtos/zephyr#78887 Signed-off-by: Krzysztof Chruściński <[email protected]>
…struct pointer Refactor RX asynchronous API function to use a pointer to the RX async data structure instead of top level data structure pointer. It improves readability with more concise code. Upstream PR: zephyrproject-rtos#78887 Signed-off-by: Krzysztof Chruściński <[email protected]>
…new features Rework driver to support new way of asynchronous RX handling. Previously RX was handled in two modes: using RXDRDY interrupt for byte counting or TIMER + PPI. Both modes had flaws. RXDRDY interrupt mode could miscalculated amount of received bytes when interrupt was not handled on time. Data was not lost but was not reported on time that could lead to issues. PPI+TIMER mode requires additional resources thus it was not the default mode. Often user was not aware of that option and was expiriencing driver RX faults. New RX mode is switching buffers when there is new data (RXDRDY event not set for given amount of time). It does not require additional resources to get precise byte counting. Additionally, this is in line with new UARTE feature (RX frame timeout) which is present in nRF54X devices. The behavior of the driver is the same for legacy devices and new one. For legacy devices k_timer periodic interrupts are used to check if there are any new bytes and it is not needed when RX frame timeout is present. Improved RX mode is enabled by default (CONFIG_UART_NRFX_UARTE_ENHANCED_RX=y) but legacy modes are still available though not recommended to be used. Note that new RX mode behaves a bit different because timeout always triggers switch of buffers which means that there will be no UART_RX_RDY events with non-zero offset. It also means that every UART_RX_RDY will be followed by UART_RX_BUF_RELEASED. After rework, driver is recommended to be used for all platforms as it performs much better and takes much less code than the second UART shim available for Nordic devices. Upstream PR: zephyrproject-rtos#78887 Signed-off-by: Krzysztof Chruściński <[email protected]>
…eout Frame timeout is a hardware feature present in newer versions of UARTE (e.g. in NRF54X platforms) for detecting idle state on RX line and ending RX after configurable timeout. Upstream PR: zephyrproject-rtos#78887 Signed-off-by: Krzysztof Chruściński <[email protected]>
…struct pointer Refactor RX asynchronous API function to use a pointer to the RX async data structure instead of top level data structure pointer. It improves readability with more concise code. Upstream PR: zephyrproject-rtos/zephyr#78887 Signed-off-by: Krzysztof Chruściński <[email protected]>
…new features Rework driver to support new way of asynchronous RX handling. Previously RX was handled in two modes: using RXDRDY interrupt for byte counting or TIMER + PPI. Both modes had flaws. RXDRDY interrupt mode could miscalculated amount of received bytes when interrupt was not handled on time. Data was not lost but was not reported on time that could lead to issues. PPI+TIMER mode requires additional resources thus it was not the default mode. Often user was not aware of that option and was expiriencing driver RX faults. New RX mode is switching buffers when there is new data (RXDRDY event not set for given amount of time). It does not require additional resources to get precise byte counting. Additionally, this is in line with new UARTE feature (RX frame timeout) which is present in nRF54X devices. The behavior of the driver is the same for legacy devices and new one. For legacy devices k_timer periodic interrupts are used to check if there are any new bytes and it is not needed when RX frame timeout is present. Improved RX mode is enabled by default (CONFIG_UART_NRFX_UARTE_ENHANCED_RX=y) but legacy modes are still available though not recommended to be used. Note that new RX mode behaves a bit different because timeout always triggers switch of buffers which means that there will be no UART_RX_RDY events with non-zero offset. It also means that every UART_RX_RDY will be followed by UART_RX_BUF_RELEASED. After rework, driver is recommended to be used for all platforms as it performs much better and takes much less code than the second UART shim available for Nordic devices. Upstream PR: zephyrproject-rtos/zephyr#78887 Signed-off-by: Krzysztof Chruściński <[email protected]>
…eout Frame timeout is a hardware feature present in newer versions of UARTE (e.g. in NRF54X platforms) for detecting idle state on RX line and ending RX after configurable timeout. Upstream PR: zephyrproject-rtos/zephyr#78887 Signed-off-by: Krzysztof Chruściński <[email protected]>
…struct pointer Refactor RX asynchronous API function to use a pointer to the RX async data structure instead of top level data structure pointer. It improves readability with more concise code. Upstream PR: zephyrproject-rtos/zephyr#78887 Signed-off-by: Krzysztof Chruściński <[email protected]>
…new features Rework driver to support new way of asynchronous RX handling. Previously RX was handled in two modes: using RXDRDY interrupt for byte counting or TIMER + PPI. Both modes had flaws. RXDRDY interrupt mode could miscalculated amount of received bytes when interrupt was not handled on time. Data was not lost but was not reported on time that could lead to issues. PPI+TIMER mode requires additional resources thus it was not the default mode. Often user was not aware of that option and was expiriencing driver RX faults. New RX mode is switching buffers when there is new data (RXDRDY event not set for given amount of time). It does not require additional resources to get precise byte counting. Additionally, this is in line with new UARTE feature (RX frame timeout) which is present in nRF54X devices. The behavior of the driver is the same for legacy devices and new one. For legacy devices k_timer periodic interrupts are used to check if there are any new bytes and it is not needed when RX frame timeout is present. Improved RX mode is enabled by default (CONFIG_UART_NRFX_UARTE_ENHANCED_RX=y) but legacy modes are still available though not recommended to be used. Note that new RX mode behaves a bit different because timeout always triggers switch of buffers which means that there will be no UART_RX_RDY events with non-zero offset. It also means that every UART_RX_RDY will be followed by UART_RX_BUF_RELEASED. After rework, driver is recommended to be used for all platforms as it performs much better and takes much less code than the second UART shim available for Nordic devices. Upstream PR: zephyrproject-rtos/zephyr#78887 Signed-off-by: Krzysztof Chruściński <[email protected]>
…eout Frame timeout is a hardware feature present in newer versions of UARTE (e.g. in NRF54X platforms) for detecting idle state on RX line and ending RX after configurable timeout. Upstream PR: zephyrproject-rtos/zephyr#78887 Signed-off-by: Krzysztof Chruściński <[email protected]>
@masz-nordic can you take a look? |
drivers/serial/uart_nrfx_uarte.c
Outdated
uint32_t divisor = 1000000 / timeout; | ||
uint32_t bauds = baudrate / divisor; | ||
|
||
return MIN(bauds, UARTE_FRAMETIMEOUT_COUNTERTOP_Msk); |
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.
Should be replaced with HAL define in next nrfx release.
@dcpleung can you take a look? |
drivers/serial/uart_nrfx_uarte.c
Outdated
uint32_t divisor = 1000000 / timeout; | ||
uint32_t bauds = baudrate / divisor; |
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.
For timeout
>= 1 second, you'll get division by 0 here.
I think it will be safer to involve 64 bits here: bauds = (uin64_t)baudrate * timeout / 1000000;
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.
drivers/serial/uart_nrfx_uarte.c
Outdated
@@ -628,36 +648,43 @@ static int uarte_nrfx_rx_counting_init(const struct device *dev) | |||
|
|||
nrfx_gppi_channel_endpoints_setup(data->async->rx.cnt.ppi, evt_addr, tsk_addr); | |||
nrfx_gppi_channels_enable(BIT(data->async->rx.cnt.ppi)); | |||
if (ret != NRFX_SUCCESS) { |
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 does not seem possible for ret
to not be NRFX_SUCCESS
at this point:
zephyr/drivers/serial/uart_nrfx_uarte.c
Lines 642 to 651 in 609b088
ret = nrfx_gppi_channel_alloc(&data->async->rx.cnt.ppi); | |
if (ret != NRFX_SUCCESS) { | |
LOG_ERR("Failed to allocate PPI Channel"); | |
nrfx_timer_uninit(&cfg->timer); | |
return -EINVAL; | |
} | |
nrfx_gppi_channel_endpoints_setup(data->async->rx.cnt.ppi, evt_addr, tsk_addr); | |
nrfx_gppi_channels_enable(BIT(data->async->rx.cnt.ppi)); | |
if (ret != NRFX_SUCCESS) { |
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.
drivers/serial/uart_nrfx_uarte.c
Outdated
@@ -834,8 +857,14 @@ static int uarte_nrfx_rx_enable(const struct device *dev, uint8_t *buf, | |||
return -EBUSY; | |||
} | |||
|
|||
#ifdef CONFIG_UART_NRFX_UARTE_ENHANCED_RX | |||
rdata->timeout = (timeout && timeout == SYS_FOREVER_US) ? |
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 timeout &&
part is unneeded. I'm not sure what was the intention here (timeout == 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.
Removed that. I've initially assumed that timeout==0 is the same as SYS_FOREVER_US but it is not stated in API so I removed it.
@@ -856,10 +886,18 @@ static int uarte_nrfx_rx_enable(const struct device *dev, uint8_t *buf, | |||
*/ | |||
if (!len) { | |||
rdata->flush_cnt -= cpy_len; | |||
notify_uart_rx_rdy(dev, cpy_len); | |||
rx_buf_release(dev, &rdata->buf); | |||
notify_rx_disable(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.
As RX no longer gets disabled here, the comment right above this block should be updated.
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.
Comment updated.
drivers/serial/uart_nrfx_uarte.c
Outdated
uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_TX, UARTE_FLAG_LOW_POWER_RX); | ||
uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_RX, UARTE_FLAG_LOW_POWER_TX); |
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 not just:
uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_TX, UARTE_FLAG_LOW_POWER_RX); | |
uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_RX, UARTE_FLAG_LOW_POWER_TX); | |
uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_TX | UARTE_FLAG_LOW_POWER_RX, 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.
Nice. Done.
drivers/serial/uart_nrfx_uarte.c
Outdated
@@ -123,7 +137,7 @@ struct uarte_async_rx { | |||
} cnt; | |||
/* Flag to ensure that RX timeout won't be executed during ENDRX ISR */ | |||
volatile bool is_in_irq; | |||
uint8_t flush_buffer[UARTE_HW_RX_FIFO_SIZE]; | |||
#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.
#endif | |
#endif /* CONFIG_UART_NRFX_UARTE_ENHANCED_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.
Done
#if !defined(UARTE_HAS_FRAME_TIMEOUT) | ||
uint32_t idle_cnt; | ||
#endif | ||
k_timeout_t timeout; |
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.
For !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX)
, a member with the same name but a different type is used. This is quite confusing. It would be good to rename one of them (it seems easier for the new one, to k_timeout
maybe).
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.
changed to timeout_us
for !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX)
case.
drivers/serial/uart_nrfx_uarte.c
Outdated
uint8_t ppi_ch_endtx; | ||
#endif | ||
atomic_t flags; |
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.
nit: place it before ppi_ch_endtx
to avoid padding.
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
drivers/serial/uart_nrfx_uarte.c
Outdated
|
||
/* If enabled then ENDTX is PPI'ed to TXSTOP */ | ||
#define UARTE_CFG_FLAG_PPI_ENDTX BIT(0) | ||
|
||
/* If enabled then TIMER and PPI is used for byte counting. */ | ||
#define UARTE_CFG_FLAG_HW_BYTE_COUNTING BIT(1) | ||
|
||
/* If enabled then cache management is required (except for dmm buffers). */ | ||
#define UARTE_CFG_FLAG_CACHEABLE BIT(3) |
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 flag doesn't seem to be used.
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.
drivers/serial/uart_nrfx_uarte.c
Outdated
#else | ||
rdata->timeout = (timeout && timeout == SYS_FOREVER_US) ? | ||
K_NO_WAIT : K_USEC(timeout / RX_TIMEOUT_DIV); | ||
rdata->idle_cnt = RX_TIMEOUT_DIV - 1; |
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
- 1
? Timeout is split intoRX_TIMEOUT_DIV
parts and theSTOPRX
task is triggered when this counter is decreased to 0, so one such part earlier. - Wouldn't it be more convenient to initialize this counter to 0 (initialization is done in several places) and check if it reaches certain value before triggering the
STOPRX
task (this happens in one place only). - I got a bit confused. The commit message states that there will be no more
UART_RX_RDY
events with non-zero offsets. But because of this timeout split, it seems to me that they still can occur. Only when the frame timeout feature is used, they cannot, because the timeout is handled differently then. What am I missing?
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.
Changed to start with 0.
Refactor RX asynchronous API function to use a pointer to the RX async data structure instead of top level data structure pointer. It improves readability with more concise code. Signed-off-by: Krzysztof Chruściński <[email protected]>
64e292f
29d2ee9
to
6288a6a
Compare
Rework driver to support new way of asynchronous RX handling. Previously RX was handled in two modes: using RXDRDY interrupt for byte counting or TIMER + PPI. Both modes had flaws. RXDRDY interrupt mode could miscalculated amount of received bytes when interrupt was not handled on time. Data was not lost but was not reported on time that could lead to issues. PPI+TIMER mode requires additional resources thus it was not the default mode. Often user was not aware of that option and was expiriencing driver RX faults. New RX mode is switching buffers when there is new data (RXDRDY event not set for given amount of time). It does not require additional resources to get precise byte counting. Additionally, this is in line with new UARTE feature (RX frame timeout) which is present in nRF54X devices. The behavior of the driver is the same for legacy devices and new one. For legacy devices k_timer periodic interrupts are used to check if there are any new bytes and it is not needed when RX frame timeout is present. Improved RX mode is enabled by default (CONFIG_UART_NRFX_UARTE_ENHANCED_RX=y) but legacy modes are still available though not recommended to be used. Note that new RX mode behaves a bit different because timeout always triggers switch of buffers which means that there will be no UART_RX_RDY events with non-zero offset. It also means that every UART_RX_RDY will be followed by UART_RX_BUF_RELEASED. After rework, driver is recommended to be used for all platforms as it performs much better and takes much less code than the second UART shim available for Nordic devices. Signed-off-by: Krzysztof Chruściński <[email protected]>
Frame timeout is a hardware feature present in newer versions of UARTE (e.g. in NRF54X platforms) for detecting idle state on RX line and ending RX after configurable timeout. Signed-off-by: Krzysztof Chruściński <[email protected]>
6288a6a
to
812e4d8
Compare
Rework driver to support new way of asynchronous RX handling. Previously RX was handled in two modes: using RXDRDY interrupt for byte counting or TIMER + PPI. Both modes had flaws. RXDRDY interrupt mode could miscalculated amount of received bytes when interrupt was not handled on time. Data was not lost but was not reported on time that could lead to issues. PPI+TIMER mode requires additional resources thus it was not the default mode. Often user was not aware of that option and was expiriencing driver RX faults.
New RX mode is switching buffers when there is new data (RXDRDY event not set for given amount of time). It does not require additional resources to get precise byte counting. Additionally, this is in line with new UARTE feature (RX frame timeout) which is present in nRF54X devices. The behavior of the driver is the same for legacy devices and new one. For legacy devices k_timer periodic interrupts are used to check if there are any new bytes and it is not needed when RX frame
timeout is present.
Improved RX mode is enabled by default (CONFIG_UART_NRFX_UARTE_ENHANCED_RX=y) but legacy modes are still
available though not recommended to be used.
Note that new RX mode behaves a bit different because timeout always triggers switch of buffers which means that there will be no UART_RX_RDY events with non-zero offset. It also means that every UART_RX_RDY will be followed by UART_RX_BUF_RELEASED.
After rework, driver is recommended to be used for all platforms as it performs much better and takes much less code than the second UART shim available for Nordic devices.
This PR is main part of #75462 which contains additional changes and significant changes in tests. It is created for ease of review as #75462 is overwhelming.
It also contains #78886.