Skip to content
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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

nordic-krch
Copy link
Contributor

@nordic-krch nordic-krch commented Sep 23, 2024

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.

@nordic-krch nordic-krch force-pushed the nrfx_uarte_rework branch 2 times, most recently from 6279c82 to 863a6e2 Compare September 26, 2024 10:25
nordic-krch added a commit to nordic-krch/fw-nrfconnect-zephyr that referenced this pull request Oct 7, 2024
…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]>
nordic-krch added a commit to nordic-krch/fw-nrfconnect-zephyr that referenced this pull request Oct 7, 2024
…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]>
nordic-krch added a commit to nordic-krch/fw-nrfconnect-zephyr that referenced this pull request Oct 7, 2024
…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]>
nordic-krch added a commit to nordic-krch/zephyr that referenced this pull request Oct 8, 2024
…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]>
nordic-krch added a commit to nordic-krch/zephyr that referenced this pull request Oct 8, 2024
…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]>
nordic-krch added a commit to nordic-krch/zephyr that referenced this pull request Oct 8, 2024
…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]>
nordic-krch added a commit to nordic-krch/fw-nrfconnect-zephyr that referenced this pull request Oct 9, 2024
…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]>
nordic-krch added a commit to nordic-krch/fw-nrfconnect-zephyr that referenced this pull request Oct 9, 2024
…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]>
nordic-krch added a commit to nordic-krch/fw-nrfconnect-zephyr that referenced this pull request Oct 9, 2024
…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]>
kl-cruz
kl-cruz previously approved these changes Oct 10, 2024
nordic-krch added a commit to nordic-krch/fw-nrfconnect-zephyr that referenced this pull request Oct 11, 2024
…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]>
nordic-krch added a commit to nordic-krch/fw-nrfconnect-zephyr that referenced this pull request Oct 11, 2024
…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]>
nordic-krch added a commit to nordic-krch/fw-nrfconnect-zephyr that referenced this pull request Oct 11, 2024
…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]>
@nordic-krch
Copy link
Contributor Author

@masz-nordic can you take a look?

masz-nordic
masz-nordic previously approved these changes Oct 14, 2024
uint32_t divisor = 1000000 / timeout;
uint32_t bauds = baudrate / divisor;

return MIN(bauds, UARTE_FRAMETIMEOUT_COUNTERTOP_Msk);
Copy link
Collaborator

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.

@nordic-krch
Copy link
Contributor Author

@dcpleung can you take a look?

Comment on lines 855 to 856
uint32_t divisor = 1000000 / timeout;
uint32_t bauds = baudrate / divisor;
Copy link
Member

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;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -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) {
Copy link
Member

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:

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) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

@@ -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) ?
Copy link
Member

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 || ?).

Copy link
Contributor Author

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);
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment updated.

Comment on lines 2115 to 2116
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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just:

Suggested change
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);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Done.

@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#endif
#endif /* CONFIG_UART_NRFX_UARTE_ENHANCED_RX */

Copy link
Contributor Author

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;
Copy link
Member

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).

Copy link
Contributor Author

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.

uint8_t ppi_ch_endtx;
#endif
atomic_t flags;
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


/* 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)
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

#else
rdata->timeout = (timeout && timeout == SYS_FOREVER_US) ?
K_NO_WAIT : K_USEC(timeout / RX_TIMEOUT_DIV);
rdata->idle_cnt = RX_TIMEOUT_DIV - 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Why - 1? Timeout is split into RX_TIMEOUT_DIV parts and the STOPRX task is triggered when this counter is decreased to 0, so one such part earlier.
  2. 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).
  3. 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?

Copy link
Contributor Author

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]>
@nordic-krch nordic-krch force-pushed the nrfx_uarte_rework branch 3 times, most recently from 29d2ee9 to 6288a6a Compare October 15, 2024 10:34
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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: UART Universal Asynchronous Receiver-Transmitter platform: nRF Nordic nRFx
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants