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

Add embedded_hal::serial implementation for uarte #281

Closed
wants to merge 19 commits into from

Conversation

lulf
Copy link
Contributor

@lulf lulf commented Jan 6, 2021

I figured 2 existing PRs on the UARTE interface wasn't enough, so I created another one 😆

This adds a sub-module named "serial" that splits TX and RX parts of
UART into separate types that implements the embedded_hal::serial
interfaces.

This allows using the nRF UART for drivers that rely on the
embedded_hal::serial traits.

The buffer management is primitive in order to support the semantics
of the more restrictive embedded_hal traits.

This is somewhat inspired by #102 and the TX/RX split is similar, but a lot more "dumb" without any advanced DMA and simplified only with the goal of having something that works with the embedded_hal traits with separate tx and rx.

@Dirbaio
Copy link
Member

Dirbaio commented Jan 6, 2021

This is unsound. A user can call UarteRx::read() to start DMA, then move the UarteRx instance. When a byte does arrive, the DMA will write it to the previous location, overwriting whatever's there.

To make this sound you need to require UarteRx and UarteTx to be pinned before use.

@Dirbaio
Copy link
Member

Dirbaio commented Jan 6, 2021

You also need to stop DMA on drop in UarteRx/UarteTx.
Otherwise a user can start DMA, then drop the UarteRx. The DMA would overwrite one byte of whatever's in the same memory location when receiving a byte.

@timokroeger
Copy link
Contributor

You could also wait for the DMA to complete and have a blocking implementation that never returns WouldBlock.
This defeats the purpose of the nb return type but you do not have to worry about possible unsoundness.

@jonas-schievink
Copy link
Contributor

In that case we should just implement blocking::serial::Write, not serial::Write

@lulf
Copy link
Contributor Author

lulf commented Jan 7, 2021

@timokroeger @jonas-schievink @Dirbaio Thank you for the review. I had not considered the case where it could be moved.
For my particular use case (which is to make it possible to use nRF UARTE for a driver relying on serial::Write) I was originally looking for the non-blocking behavior.

Could an alternative approach to pinning, be to pass the tx and rx buffers to split() as I have pushed now? I realize this move more work to the user on initialization, but it seems more flexible and could potentially allow implementations to do larger DMA transfers if buffer sizes allow it. I'm not sure if it would still be sound/unsound (intuitively I think the compiler would prevent moving the mutable tx/rx buf slices?).

I have also made an attempt at implementing Drop.

@jonas-schievink
Copy link
Contributor

They can still be leaked, and then the mutable slices can be freely used again, so this is still unsound

@lulf
Copy link
Contributor Author

lulf commented Jan 11, 2021

@jonas-schievink I've added a required 'static lifetime for the buffers, I would like to avoid introducing the heapless crate as a dependency. I wasn't sure if it would be more appropriate to rely on the embedded_dma traits instead, wdyt?

I've also made it so that one can provide a larger tx buffer and do multibyte DMA if desirable within the contract of the serial::Write trait.

Copy link
Contributor

@jonas-schievink jonas-schievink left a comment

Choose a reason for hiding this comment

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

Please move the duplicated implementation code into common functions, we don't really want to maintain two implementations of the whole TX and RX logic

nrf-hal-common/src/uarte.rs Outdated Show resolved Hide resolved
nrf-hal-common/src/uarte.rs Outdated Show resolved Hide resolved
nrf-hal-common/src/uarte.rs Outdated Show resolved Hide resolved
nrf-hal-common/src/uarte.rs Outdated Show resolved Hide resolved
nrf-hal-common/src/uarte.rs Outdated Show resolved Hide resolved
@jonas-schievink jonas-schievink added the breaking change Requires a major semver bump label Jan 11, 2021
This adds a sub-module named "serial" that splits TX and RX parts of
UART into separate types that implements the embedded_hal::serial
interfaces.

This allows using the nRF UART for drivers that rely on the
embedded_hal::serial traits.

The buffer management is primitive in order to support the semantics
of the more restrictive embedded_hal traits.
* Allow making multi-byte DMA transfers by starting DMA only on flush().
  Read is still limited by the embedded_hal trait, so will still read
  only 1 byte at a time.
* Auto derive blocking trait from non-blocking version.
* Remove explicit lifetime for tx/rx structs
* Refactor write/read logic into functions that are reused in the
  existing combined Uarte implementation and the UartTx/UarteRx
  implementation.
lulf and others added 2 commits January 12, 2021 14:36
Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
nrf-hal-common/src/uarte.rs Outdated Show resolved Hide resolved
nrf-hal-common/src/uarte.rs Outdated Show resolved Hide resolved
nrf-hal-common/src/uarte.rs Show resolved Hide resolved
@hargoniX
Copy link
Contributor

Hi, I stumbled upon this PR during the rewrite of the discovery book to micro:bit v1/v2. Basically I need to write code that is compatible with both the microbit v1 (nrf51) and v2 (nrf52833). Since the nrf52833-hal exposes only the UARTE peripheral the read() methods of both HALs are however not compatible with each other (UARTE requires an rx buffer, nrf51 just returns a single byte). Is there any chance this (or the other two PRs mentioned in the original PR message...which I couldn't find) will be merged soon / can I do something to make that happen? It's sadly really blocking me on the discovery book at the moment.

Comment on lines +443 to +444
pub trait Instance: Deref<Target = uarte0::RegisterBlock> + sealed::Sealed {
fn ptr() -> *const uarte0::RegisterBlock;
Copy link
Contributor

Choose a reason for hiding this comment

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

We shouldn't need both the Deref supertrait and the ptr method here

Copy link
Contributor

Choose a reason for hiding this comment

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

Is that it already? Shall I just fork @lulf then and "re PR" this I guess?

Copy link
Contributor

Choose a reason for hiding this comment

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

I should take a closer look at this, but that's one thing I noticed

Copy link
Contributor

Choose a reason for hiding this comment

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

So after reading a bit through the current implementation I saw that it still uses this Deref super trait https://github.com/nrf-rs/nrf-hal/blob/master/nrf-hal-common/src/uarte.rs#L452 (as well as other peripherals) so I'm quite unsure how to get rid off it? (I do get why the ptr() can be get rid off of course). I'm unfortunately not really familiar with the project so could you go into a bit of detail as to why we don't need it here?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd like to remove the Deref supertrait, since it's essentially redundant with the ptr method. I guess this doesn't have to be done in this PR though.

pub fn split(
self,
tx_buf: &'static mut [u8],
rx_buf: &'static mut [u8; 1],
Copy link
Contributor

Choose a reason for hiding this comment

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

The docs should explain why this is a fixed-size array instead of a slice


let in_progress = uarte.events_txstarted.read().bits() == 1;
// Stop any ongoing transmission
if in_progress {
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't we reset events_txstarted in this if block? Otherwise it seems to me that it'd stay on indefinitely


let in_progress = uarte.events_rxstarted.read().bits() == 1;
// Stop any ongoing reception
if in_progress {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here for events_rxstarted

@hargoniX
Copy link
Contributor

Continued in #343.

@lulf
Copy link
Contributor Author

lulf commented Jul 29, 2021

@hargoniX Sorry, but the PR was not really abandoned from my POV. I didn't get any new reviews on it and was told it would need to wait for a breaking release in order to be merged. Then I didn't really see that it had conflicts until I saw your comment just now.

I've been on holiday the past few weeks, so I haven't been able to respond until now on your comment.

However, I'm happy if you want to do the remaining fixes :) Next time, please ping/mention the author directly and add a longer "timeout", in case the person you ping is temporarily away and actually intended to work on it.

@lulf lulf closed this Jul 29, 2021
bors bot added a commit that referenced this pull request Aug 12, 2021
343:  Add embedded_hal::serial implementation for uarte (continuation) r=jonas-schievink a=hargoniX

Since #281 has been abandoned by the author this is a continuation of his work in a seperate PR.

Co-authored-by: Ulf Lilleengen <ulf.lilleengen@gmail.com>
Co-authored-by: Henrik Böving <hargonix@gmail.com>
bors bot added a commit that referenced this pull request Aug 12, 2021
343:  Add embedded_hal::serial implementation for uarte (continuation) r=jonas-schievink a=hargoniX

Since #281 has been abandoned by the author this is a continuation of his work in a seperate PR.

Co-authored-by: Ulf Lilleengen <ulf.lilleengen@gmail.com>
Co-authored-by: Henrik Böving <hargonix@gmail.com>
bors bot added a commit to nrf-rs/microbit that referenced this pull request Aug 22, 2021
63: Initial UART(E) implementation r=robyoung a=hargoniX

This implements an abstraction for UARTE, very similar to the general I2C abstracttion. At the moment the example doesn't compile for nrf52833 yet, once nrf-rs/nrf-hal#281 is finally merged that should be fairly trivial to do though.

Co-authored-by: Henrik Böving <hargonix@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking change Requires a major semver bump
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants