|
| 1 | +# Serial design document |
| 2 | + |
| 3 | +# Table of contents |
| 4 | + |
| 5 | +1. [Serial design document](#serial-design-document). |
| 6 | +1. [Table of contents](#table-of-contents). |
| 7 | + 1. [Revision history](#revision-history). |
| 8 | +1. [Introduction](#introduction). |
| 9 | + 1. [Overview and background](#overview-and-background). |
| 10 | + 1. [Requirements and assumptions](#requirements-and-assumptions). |
| 11 | +1. [System architecture and high-level design](#system-architecture-and-high-level-design). |
| 12 | + 1. [System architecture and component interaction](#system-architecture-and-component-interaction). |
| 13 | +1. [Detailed design](#detailed-design). |
| 14 | +1. [Usage scenarios and examples](#usage-scenarios-and-examples). |
| 15 | +1. [Other Work](#other-work). |
| 16 | + 1. [Related work-Simplification of retarget code](#related-work-simplification-of-retarget-code). |
| 17 | + 1. [Future Work-Deprecation of Stream](#future-work-deprecation-of-stream). |
| 18 | +1. [Roll-out Plan](#roll-out-plan). |
| 19 | +1. [Tools and configuration changes](#tools-and-configuration-changes). |
| 20 | +1. [Other information](#other-information). |
| 21 | + 1. [Deprecations](#deprecations). |
| 22 | + 1. [References](#references). |
| 23 | + |
| 24 | + |
| 25 | +### Revision history |
| 26 | + |
| 27 | +1.0 - Initial version - Evelyne Donnaes - 04/10/2019 |
| 28 | + |
| 29 | +# Introduction |
| 30 | + |
| 31 | +### Overview and background |
| 32 | + |
| 33 | +Mbed OS contains multiple serial classes and multiple ways of printing information to the console. Serial classes have evolved over the years to add new functinality to existing classes and compensate for limitations. The result is that there are now multiple variants, not always well documented; therefore it is not clear which class shoud be used for what purpose. Futhermore, some of the classes pull substantial dependencies whose benefits are not very clear, for example standard library like objects and abstractions, and parts of the retarget layer. |
| 34 | + |
| 35 | +This document collects proposals made by various contributors (see [references](#references)) and presents a simplified serial class hierarchy. The aim is to offer two public classes, one for unbuffered I/O and the other for buffered I/O, with a clear definition of which one should be used when. All other serial classes are deprecated and should be removed at a later stage. In addition, the serial classes dependencies will be optimised to only include components that provide valuable functionality. |
| 36 | + |
| 37 | +#### Serial classes in Mbed OS 5.14 |
| 38 | + |
| 39 | +**Serial** |
| 40 | + |
| 41 | +`Serial` provides unbuffered I/O and is using blocking HAL calls. It only works for simple printing. |
| 42 | + |
| 43 | +Its main limitations are: |
| 44 | +- It uses a mutex lock so it cannot be used from interrupts. |
| 45 | +- It wastes a lot of CPU time spinning waiting for serial output and it cannot be used reliably for input because it needs interrupt-speed response on read calls but its high-level API cannot be used from interrupts. |
| 46 | +- It lacks buffering so it cannot be used reliably for input and output without flow control. |
| 47 | +- It pulls in the C library stdio system because it uses `Stream`. |
| 48 | + |
| 49 | +**UARTSerial** |
| 50 | + |
| 51 | +`UARTSerial` provides buffered I/O. It can be used reliably for input from non-interrupt context and to avoid excess spinning waiting for transmission buffer space. It also does better blocking and correct POSIX-like `FileHandle` semantics. |
| 52 | + |
| 53 | +It does not use `Stream` so it has the advantage of not pulling in the C stdio library. The drawback is that it loses the ability to call printf directly. |
| 54 | + |
| 55 | +**RawSerial** |
| 56 | + |
| 57 | +`RawSerial` provides unbuffered I/O. It has no locks so it is safe to use in interrupts provided that it is the only instance that is using that serial port. |
| 58 | + |
| 59 | +### Requirements and assumptions |
| 60 | + |
| 61 | +None |
| 62 | + |
| 63 | +# System architecture and high-level design |
| 64 | + |
| 65 | +Applications will have a choice between two public serial classes: |
| 66 | +- `BufferedSerial` for a normal application |
| 67 | +- `UnbufferedSerial` for an interrupt driven application or one that needs to have more control |
| 68 | + |
| 69 | +### System architecture and component interaction |
| 70 | + |
| 71 | +The below diagram shows the inheritance hierarchy for the serial classes. |
| 72 | + |
| 73 | + |
| 74 | + |
| 75 | +`MinimalSerial` is an abstract class that extracts basics I/O functionality from `SerialBase` so it can be re-used for a minimal serial console in the retarget code. |
| 76 | + |
| 77 | +# Detailed design |
| 78 | + |
| 79 | +### Detailed design : BufferedSerial |
| 80 | + |
| 81 | +`BufferedSerial` is `UARTSerial` renamed to convey the original purpose of the class. In addition, the new class will allow configurability of the transmit and receive buffer size so that each instance of the class can configure its buffer size according to its intended usage, for example a modem may need a large input buffer and a console may not need any input buffer. |
| 82 | + |
| 83 | +The below diagram shows the detailed inheritance hierarchy for `BufferedSerial`. |
| 84 | + |
| 85 | + |
| 86 | + |
| 87 | +**API description** |
| 88 | + |
| 89 | +This is the new constructor for `BufferedSerial` to pass in the serial buffers size. |
| 90 | + |
| 91 | +```C |
| 92 | + /** Create a BufferedSerial port, connected to the specified transmit and receive pins, with a particular baud rate. |
| 93 | + * @param tx Transmit pin |
| 94 | + * @param rx Receive pin |
| 95 | + * @param baud The baud rate of the serial port (optional, defaults to MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE) |
| 96 | + * @param rx_buffer_size the size of the receive buffer (optional, defaults to MBED_CONF_DRIVERS_UART_SERIAL_RXBUF_SIZE) |
| 97 | + * @param tx_buffer_size the size of the transmit buffer (optional, defaults to MBED_CONF_DRIVERS_UART_SERIAL_TXBUF_SIZE) |
| 98 | + */ |
| 99 | + BufferedSerial(PinName tx, |
| 100 | + PinName rx, |
| 101 | + int baud = MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE, |
| 102 | + uint32_t rx_buffer_size = MBED_CONF_DRIVERS_UART_SERIAL_RXBUF_SIZE, |
| 103 | + uint32_t tx_buffer_size = MBED_CONF_DRIVERS_UART_SERIAL_TXBUF_SIZE); |
| 104 | +``` |
| 105 | +
|
| 106 | + The rest of the class API is that of `UARTSerial` and is described [here](https://os.mbed.com/docs/mbed-os/v5.14/mbed-os-api-doxy/classmbed_1_1_u_a_r_t_serial.html). |
| 107 | +
|
| 108 | +
|
| 109 | +### Detailed design : UnbufferedSerial |
| 110 | +
|
| 111 | +`UnbufferedSerial` is a new class that adds a `FileHandle` interface to `RawSerial`. `RawSerial` is the preferred implementation for unbuffered I/O but it lacks a `FileHandle` interface and so it cannot be used by the retarget code. Instead the retarget code uses a non-public `DirectSerial` class which adds to the complexity of classes and should be removed. `UnbufferedSerial` will not provide any of the printf methods that `RawSerial` has; applications should use _fdopen_ to get a _FILE*_ to use the printf function, see [Usage scenarios and examples](#usage-scenarios-and-examples). |
| 112 | +
|
| 113 | +The below diagram shows the detailed inheritance hierarchy for `UnbufferedSerial`. |
| 114 | +
|
| 115 | + |
| 116 | +
|
| 117 | +**API description** |
| 118 | +
|
| 119 | +This is the proposed API for the `UnbufferedSerial` class. |
| 120 | +
|
| 121 | +```C |
| 122 | +class UnbufferedSerial: public SerialBase, public FileHandle, private NonCopyable<UnbufferedSerial> { |
| 123 | +
|
| 124 | +public: |
| 125 | + /** Create a UnbufferedSerial port, connected to the specified transmit and receive pins, with the specified baud. |
| 126 | + * |
| 127 | + * @param tx Transmit pin |
| 128 | + * @param rx Receive pin |
| 129 | + * @param baud The baud rate of the serial port (optional, defaults to MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE) |
| 130 | + * |
| 131 | + * @note |
| 132 | + * Either tx or rx may be specified as NC if unused |
| 133 | + */ |
| 134 | + UnbufferedSerial(PinName tx, PinName rx, int baud = MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE); |
| 135 | +
|
| 136 | + /** Write a char to the serial port |
| 137 | + * |
| 138 | + * @param c The char to write |
| 139 | + * |
| 140 | + * @returns The written char or -1 if an error occurred |
| 141 | + */ |
| 142 | + int putc(int c); |
| 143 | +
|
| 144 | + /** Read a char from the serial port |
| 145 | + * |
| 146 | + * @returns The char read from the serial port |
| 147 | + */ |
| 148 | + int getc(); |
| 149 | +
|
| 150 | + /** Write a string to the serial port |
| 151 | + * |
| 152 | + * @param str The string to write |
| 153 | + * |
| 154 | + * @returns 0 if the write succeeds, EOF for error |
| 155 | + */ |
| 156 | + int puts(const char *str); |
| 157 | +
|
| 158 | + virtual ssize_t write(const void *buffer, size_t size); |
| 159 | + virtual ssize_t read(void *buffer, size_t size); |
| 160 | + virtual off_t seek(off_t offset, int whence = SEEK_SET); |
| 161 | + virtual off_t size(); |
| 162 | + virtual int isatty(); |
| 163 | + virtual int close(); |
| 164 | + virtual short poll(short events) const; |
| 165 | +
|
| 166 | +#if !(DOXYGEN_ONLY) |
| 167 | +protected: |
| 168 | +
|
| 169 | + /* Acquire exclusive access to this serial port |
| 170 | + */ |
| 171 | + virtual void lock(void); |
| 172 | +
|
| 173 | + /* Release exclusive access to this serial port |
| 174 | + */ |
| 175 | + virtual void unlock(void); |
| 176 | +#endif |
| 177 | +}; |
| 178 | +``` |
| 179 | + |
| 180 | +### Detailed design : MinimalSerial |
| 181 | + |
| 182 | +The `MinimalSerial` class is a minimal abstract base class that provides basics I/O functionality. All other serial classes derive from it. |
| 183 | + |
| 184 | +**API description** |
| 185 | + |
| 186 | +This is the proposed API for the `MinimalSerial` class. Only two methods are provided to output `_base_putc()` and receive `_base_getc()` characters on the UART. |
| 187 | + |
| 188 | +```C |
| 189 | +class MinimalSerial : private NonCopyable<MinimalSerial> { |
| 190 | +protected: |
| 191 | + MinimalSerial(PinName tx, PinName rx, int baud); |
| 192 | + virtual ~MinimalSerial(); |
| 193 | + int _base_getc(); |
| 194 | + int _base_putc(int c); |
| 195 | +}; |
| 196 | +``` |
| 197 | + |
| 198 | +### Detailed design : MinimalConsole |
| 199 | + |
| 200 | +`MinimalConsole` is an internal concrete class that derives from `MinimalSerial` and provides basic console functionality that can be used by the retarget code for resources constrained targets, see [Simplication of retarget code](#simplification-of-retarget-code). |
| 201 | + |
| 202 | +**API description** |
| 203 | + |
| 204 | +```C |
| 205 | +class MinimalConsole : public MinimalSerial, private NonCopyable<MinimalConsole> { |
| 206 | +public: |
| 207 | + MinimalConsole(PinName tx, PinName rx, int baud); |
| 208 | + virtual ~MinimalConsole(); |
| 209 | + int getc(); |
| 210 | + int putc(int c); |
| 211 | +}; |
| 212 | +``` |
| 213 | + |
| 214 | +The constructor provides arguments to create a serial port connected to the specified transmit and receive pins and set to the specified baud rate. If a target has serial support, by default a serial port is used for the console and the pins and settings for the port selection come from target header files and JSON settings. |
| 215 | + |
| 216 | +# Usage scenarios and examples |
| 217 | + |
| 218 | +### Scenario 1 `Printing to stdout` |
| 219 | + |
| 220 | +The below example shows pseudocode for how to print `Hello` to stdout using the new API. Printf cannot be called directly; instead the application first calls _fdopen_ to get a _FILE*_ to use the printf function. |
| 221 | + |
| 222 | +```C |
| 223 | +BufferedSerial serial(RX, TX, 115200); |
| 224 | +FILE *out = fdopen(&serial, "w"); |
| 225 | +fprintf(out, "Hello"); |
| 226 | +``` |
| 227 | +and similarly for `UnbufferedSerial` |
| 228 | +
|
| 229 | +```C |
| 230 | +UnbufferedSerial serial(RX, TX, 115200); |
| 231 | +FILE *out = fdopen(&serial, "w"); |
| 232 | +fprintf(out, "Hello"); |
| 233 | +``` |
| 234 | + |
| 235 | +# Other Work |
| 236 | + |
| 237 | +## Related work-Simplification of retarget code |
| 238 | + |
| 239 | +In Mbed, retargeting is implemented by redefining the system I/O functions. |
| 240 | +The `FileHandle` abstract class provides an interface with file-like operations, such as read and write that can be redefined by the retarget code. The high-level library functions perform input/output by calling the low-level functions which use the system I/O functions to interface with hardware. |
| 241 | + |
| 242 | +The relationship of `FileHandle` and other APIs is as follows: |
| 243 | + |
| 244 | + |
| 245 | + |
| 246 | +The POSIX layer and the static array of `FileHandle*` allocated by the retarget code for file handling consume a lot of memory. A constrained target that only uses a console does not need file handling and should be able to turn this functionality off. |
| 247 | + |
| 248 | + A new configuration parameter is introduced to compile the file handling code out. The application can override the default configuration in mbed_app.json as follows: |
| 249 | + |
| 250 | +```C |
| 251 | + "target_overrides": { |
| 252 | + "*": { |
| 253 | + "platform.stdio-minimal-console-only": true |
| 254 | + } |
| 255 | + } |
| 256 | +``` |
| 257 | +
|
| 258 | +There are two approaches to achieve a minimal retarget layer. You can: |
| 259 | +1. Redefine the low-level library functions. A detailed description can be found [here](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0475m/chr1358938931411.html) |
| 260 | +1. Redefine the system I/O functions. |
| 261 | +
|
| 262 | +A minimal console only needs to write a single character at a time. So redefining the default fputc() to directly write to the serial port if the output stream is stdout and bypassing the system I/O functions should achieve higher memory savings. If we take this approach, we will have to rework some error handlers that rely on the POSIX form of `write(STDOUT_FILENO, buf, len)` to do emergency printing. |
| 263 | +
|
| 264 | +The second solution keeps the POSIX layer with the main saving coming from dropping the use of `FileHandle` and the file handle table itself. In this case, `write()` would call `MinimalConsole::putc` in a loop. |
| 265 | +
|
| 266 | +List of tasks to simplify the retarget code: |
| 267 | +
|
| 268 | +- Use `UnbufferedSerial` instead of `DirectSerial` |
| 269 | +- Remove `DirectSerial` |
| 270 | +- Add a configuration parameter to compile out file handling feature |
| 271 | + - Alternative uses weak console_putc/getc functions |
| 272 | +
|
| 273 | +## Future work-Deprecation of Stream |
| 274 | +
|
| 275 | +The `Stream` class pulls in the <stdio.h> library. It should be replaced by drivers implementing `FileHandle` directly and applications using `fdopen(FileHandle *)` to get a `FILE*` to use full C/C++ stream features. |
| 276 | +
|
| 277 | +`Stream` is currently used by the following classes: |
| 278 | +- `Serial` |
| 279 | +- `USBKeyboard` |
| 280 | +- `USBMouseKeyboard` |
| 281 | +- `USBSerial` |
| 282 | +
|
| 283 | +Deprecation of `Stream` should be planned in a future release. |
| 284 | +
|
| 285 | +# Roll-out Plan |
| 286 | +
|
| 287 | +This feature will be implemented in different phases as follows: |
| 288 | +
|
| 289 | +Phase 1: |
| 290 | +- [Deprecation of APIs](#Deprecations) |
| 291 | +- Implementation of new class `UnbufferedSerial` |
| 292 | +- Implementation of new class `BufferedSerial` |
| 293 | +- Use `UnbufferedSerial` instead of `DirectSerial` in retarget code |
| 294 | +
|
| 295 | +Phase 2: |
| 296 | +- Removal of deprecated APIs |
| 297 | +
|
| 298 | +# Tools and configuration changes |
| 299 | +
|
| 300 | +### Tool changes |
| 301 | +
|
| 302 | +None |
| 303 | +
|
| 304 | +### Configuration changes |
| 305 | +
|
| 306 | +None |
| 307 | +
|
| 308 | +# Other information |
| 309 | +
|
| 310 | +### Deprecations |
| 311 | +
|
| 312 | +The following APIs will be deprecated: |
| 313 | +- `Serial` class |
| 314 | +- `RawSerial` class |
| 315 | +- `UARTSerial` class |
| 316 | +
|
| 317 | +### References |
| 318 | +
|
| 319 | +https://github.com/ARMmbed/mbed-os/pull/5655 |
| 320 | +
|
| 321 | +[Tailoring input/output functions in the C and C++ libraries](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0475m/chr1358938931411.html) |
0 commit comments