[RFC] Revised HILs part 1: UART

37 views
Skip to first unread message

Amit Levy

unread,
Sep 13, 2016, 2:38:53 AM9/13/16
to tock...@googlegroups.com
As Phil has pointed out, the interfaces in the HIL crate, which define
the barrier between portable drivers and chip-specific controllers, was
hastily written and should be revisited.

To start us off, the UART trait.

Currently, the UART looks like this:

pub trait UART {
fn init(&mut self, params: UARTParams);
fn send_byte(&self, byte: u8);
fn send_bytes(&self, bytes: &'static mut [u8], len: usize);
fn read_byte(&self) -> u8;
fn rx_ready(&self) -> bool;
fn tx_ready(&self) -> bool;
fn enable_rx(&self);
fn disable_rx(&mut self);
fn enable_tx(&self);
fn disable_tx(&mut self);
}

pub trait Client {
fn read_done(&self, byte: u8);
fn write_done(&self, buffer: &'static mut [u8]);
}

## No synchronous operations

The most significant problem is that that there are synchronous
interfaces for both RX and TX. With buses, like UART, we should not
allow synchronous operations. Even a single byte at 115200 baud rate
will take on the order of 70 microseconds to transmit, which is an order
of magnitude slower than event handling in Tock on the sam4l.

Getting rid of synchronous operations means we could get rid of, at
least, `send_byte`, `tx_ready` and `rx_ready`.

This also means it's probably not useful to expose `enable_tx` and
`disable_tx` since the implementation knows when it's done with TX.
(enable/disable RX should remain, though).

## A better buffer type

We currently only allow one type of buffer to be used for TX --- a
mutable slice. We chose this, initially, because the client must
relinquish access to the buffer while it's used by the UART
implementation, in most cases the client wants to be able modify the
buffer, and the implementation can only return something of the same
type it was handed. So a mutable slice seemed the most prudent.

However, it is restrictive in a few ways.

First, it means a client cannot directly transmit an immutable buffer,
like a statically allocated string, even though the hardware would allow
it. It must copy into a mutable slice.

Second, the client must make sure to locate the data to transmit at the
beginning of the buffer since there is no offset parameter to
`send_bytes` and passing a sliced buffer would mean the client looses
access to the full buffer forever.

Instead, I propose we introduce a new buffer type that solves both of
these problems:

enum EitherBuffer {
Immutable(Buffer<&'static [u8]>),
Mutable(Buffer<&'static mut [u8]>)
}

struct Buffer<I: AsRef<[u8]>> {
buffer: I,
offset: usize,
limit: usize
}

The Buffer type can hold either a mutable or immutable slice, but it
knows which one so it can return the appropriate one to the client. It
has additional markers for the cursor and limit, so a sub-slice of a
Buffer would not mean the client looses access to the full Buffer.

It's fairly straight forward to implement the slice traits for `Buffer`
such that subslicing

This seems like a useful construct for basically any communication bus,
so I don't see a reason not to reflect this type in the client as well
(as opposed to just using it behind the scenes).

## Receive buffer

Currently it's only possible to read one byte at a time. This makes
sense for the console (our flagship UART driver) since user input isn't
a fix length. However, for most other protocols, I think it'll be both
more ergonomic and better for power consumption to read a fixed amount
at a time (perhaps allowing less with a timeout? not sure how this would
work...)

Brad, since you wrote the nrf51822 serialization driver, which also uses
UART, I think you are most qualified to comment on this. Doesn't that
protocol have fixed length buffers (after a single length byte)? Would
such a change improve that driver (and presumably other similar ones)?

# Proposed interface

pub trait UART {
fn init(&mut self, params: UARTParams);
fn send_bytes(&self, bytes: EitherBuffer);
// The number of bytes to expect will be the length of the buffer
fn receive_bytes(&self, buffer: Buffer<&'static mut [u8]>);
}

pub trait Client {
fn read_done(&self, byte: Buffer<&'static mut [u8]>);
fn write_done(&self, buffer: EitherBuffer);
}

## Open questions

What should `UARTParams` contain? For example, if parity and flow
control application on all/most chips? Should there be an option to have
flow control for just one, but not both, of RX or TX?

-Amit

Brad Campbell

unread,
Sep 13, 2016, 12:37:09 PM9/13/16
to Amit Levy, 'Eitan Mosenkis' via Tock Embedded OS Development Discussion

Brad, since you wrote the nrf51822 serialization driver, which also uses 
UART, I think you are most qualified to comment on this. Doesn't that 
protocol have fixed length buffers (after a single length byte)? Would 
such a change improve that driver (and presumably other similar ones)? 
Yes it sends a length byte first and then waits for that many bytes over UART. I basically had to implement the functionality you are talking about, so having that pushed lower into the UART driver would be great.



Immutable(Buffer<&'static [u8]="">),
Mutable(Buffer<&'static mut="" [u8]="">)
}

struct Buffer<[u8]>> {
fn receive_bytes(&self, buffer: Buffer<&'static mut="" [u8]="">);
}

pub trait Client {
fn read_done(&self, byte: Buffer<&'static mut="" [u8]="">);
fn write_done(&self, buffer: EitherBuffer);
}

## Open questions

What should `UARTParams` contain? For example, if parity and flow
control application on all/most chips? Should there be an option to have
flow control for just one, but not both, of RX or TX?

-Amit

--
You received this message because you are subscribed to the Google Groups "Tock Embedded OS Development Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to tock-dev+u...@googlegroups.com.
To post to this group, send email to tock...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/tock-dev/cb30b7be-8760-2a4d-9016-59aa3587a592%40amitlevy.com.
For more options, visit https://groups.google.com/d/optout.

Branden Ghena

unread,
Sep 23, 2016, 1:02:19 PM9/23/16
to Tock Embedded OS Development Discussion
Since I need to use a bidirectional asynchronous UART for a GPS driver on signpost, I've been working on these UART changes in a branch `uart-async`

I ended up making some modifications from what Amit had originally proposed. Take a look and let me know what you think.

Thanks
Branden
Reply all
Reply to author
Forward
0 new messages