Doc: migration 5

Migrate pages:
	* Delayed ACK and TCP Performance
	* TCP Network Performance
	* NxWM Threading
	* Framebuffer Character Driver

From Confluence wiki to official wiki
This commit is contained in:
Ludovic Vanasse 2024-11-09 15:40:53 -05:00 committed by Xiang Xiao
parent 6dd313939f
commit dbeaec4cdc
12 changed files with 788 additions and 0 deletions

View file

@ -0,0 +1,195 @@
===============================
Delayed ACK and TCP Performance
===============================
.. warning::
Migrated from:
https://cwiki.apache.org/confluence/display/NUTTX/Delayed+ACK+and+TCP+Performance
uIP and NuttX
=============
The heart of the NuttX IP stack derived from Adam Dunkel's tiny `uIP
stack <http://sourceforge.net/projects/uip-stack/>`_ back at version 1.0.
The NuttX TCP/IP stack contains the uIP TCP state machine and
some uIP "ways of doing things," but otherwise, there is
now little in common between these two designs.
**NOTE**: uIP is also built into Adam Dunkel's
`Contiki <http://contiki.sourceforge.net/docs/2.6/a01793.html>`_
operating system.
uIP, Delayed ACKs, and Split Packets
====================================
In uIP, TCP packets are sent and ACK'ed one at a time.
That is, after one TCP packet is sent, the next packet cannot
be sent until the previous packet has been ACKed by the
receiving side.
The TCP protocol, of course, supports sending multiple packets
which can be ACKed be the receiving time asynchronously.
This one-packet-at-a-time logic is a simplification in the
uIP design; because of this, uIP needs only a single packet
buffer any you can use uIP in even the tiniest environments.
This is a good thing for the objectives of uIP.
Improvements in packet buffering is the essential improvement
that you get if upgrade from Adam Dunkel's uIP to his
`lwIP <http://savannah.nongnu.org/projects/lwip/>`_ stack.
The price that you pay is in memory usage.
This one-at-a-time packet transfer does create a performance
problem for uIP:
RFC 1122 states that a host may delay ACKing a packet for up
to 500ms but must respond with an ACK to every second segment.
In the baseline uIP, the effectively adds a one half second
delay between the transfer of every packet to a recipient
that employs this delayed ACK policy!
uIP has an option to work around this:
It has logic that can be enable to split each packet into half,
sending half as much data in each packet.
Sending more, smaller packets does not sound like a performance
improvement.
This tricks the recipient that follows RFC 1122 into receiving
the two, smaller back-to-back packets and ACKing the second
immediately.
References: `uip-split.c <http://contiki.sourceforge.net/docs/2.6/a00427_source.html>`_
and `uip-split.h <http://contiki.sourceforge.net/docs/2.6/a00428.html>`_.
The NuttX TCP/IP Stack and Delay ACKs
=====================================
The NuttX, low-level TCP/IP stack does not have the
limitations of the uIP TCP/IP stack.
It can send numerous TCP/IP packets regardless of
whether they have been ACKed or not.
That is because in NuttX, the accounting for which
packets have been ACKed and which have not has been
moved to a higher level in the architecture.
NuttX includes a standard, BSD socket interface on top
of the low-level TCP/IP stack.
It is in this higer-level, socket layer where the ACK
accounting is done, specifically in the function
`send() <http://pubs.opengroup.org/onlinepubs/009695399/functions/send.html>`_.
If you send a large, multi-packet buffer via ``send()``,
it will be broken up into individual packets and each
packet will be sent as quickly as possible, with no
concern for whether the previous packet has been ACKed or not.
However, the NuttX ``send()`` function will not return to
the caller until the final packet has been ACKed.
It does this to assure that the callers data was sent
successfully (or not).
This behavior means that if an odd number of packets
were sent, there could still be a delay after the final
packet before ``send()`` receives the ACK and returns.
So the NuttX approach is similar to the uIP way of doing
things, but does add one more buffer, the user provided
buffer to ``send()``, that can be used to improve TCP/IP
performance (of course, this user provided buffer is
also required by in order to be compliant with ``send()``""
`specification <http://pubs.opengroup.org/onlinepubs/009695399/functions/send.html>`_.
The NuttX Split Packet Configuration
====================================
But what happens if all of the user buffer is smaller than
the MSS of one TCP packet?
Suppose the MTU is 1500 and the user I/O buffer is only
512 bytes?
In this case, ``send()`` performance degenerates to the same
behavior as uIP:
An ACK is required for each packet before ``send()`` can
return and before ``send()`` can be called again to send
the next packet.
And the fix? A fix has recently been contributed by
Yan T that works in a similar way to uIP split
packet logic:
In ``send()``, the logic normally tries to send a full packet
of data each time it has the opportunity to do so.
However, if the configuration option ``CONFIG_NET_TCP_SPLIT=y``
is defined, the behavior of ``send()`` will change in the
following way:
* ``send()`` will keep track of `even` and `odd` packets; `even`
packets being those that we do not expect to be ACKed
and `odd` packets being the those that we do expect to
be ACKed.
* ``send()`` will then reduce the size of even packets as
necessary to assure that an even number of packets is
always sent. Every call to send will result in an even
number of packets being sent.
This clever solution tricks the RFC 1122 recipient in the
same way that uIP split logic does.
So if you are working with hosts the following the RFC 1122
ACKing behavior and you have MSS sizes that are larger that
the average size of the user buffers, then your throughput
can probably be greatly improved by enabling ``CONFIG_NET_TCP_SPLIT=y``
NOTE: NuttX is `not` an RFC 1122 recipient; NuttX will ACK
every TCP/IP packet that it receives.
Write Buffering
===============
The best technical solution to the delayed ACK problem
would be to support `write buffering`.
Write buffering is enabled with ``CONFIG_NET_TCP_WRITE_BUFFERS``.
If this option is selected, the NuttX networking layer will
pre-allocate several write buffers at system initialization
time. The sending a buffer of data then works like this:
* ``send()`` (1) obtains a pre-allocated write buffer from a free
list, and then (2) simply copies the buffer of data that the
user wishes to send into the allocated write buffer. If no
write buffer is available, ``send()`` would have to block waiting
for free write buffer space.
* ``send()`` then (3) adds the write buffer to a queue of outgoing
data for a TCP socket. Each open TCP socket has to support
such a queue. ``send()`` could then (4) return success to the
caller (even thought the transfer could still fail later).
* Logic outside of the ``send()`` implementation manages the actual
transfer of data from the write buffer. When the Ethernet
driver is able to send a packet on the TCP connection, this
external logic (5) copies a packet of data from the write
buffer so that the Ethernet driver can perform the
transmission (a `zero-copy` implementation would be preferable).
Note that the data has to remain in the write buffer for now;
it may need to be re-transmitted.
* This external logic would also manage the receipt TCP ACKs.
When TCP peer acknowledges the receipt of data, the
acknowledged portion of the data can the (6) finally
be deleted from the write buffer.
The following options configure TCP write buffering:
* ``CONFIG_NET_TCP_WRITE_BUFSIZE``: The size of one TCP write buffer.
* ``CONFIG_NET_NTCP_WRITE_BUFFERS``: The number of TCP
write buffers (may be zero to disable TCP/IP write buffering)
NuttX also supports TCP read-ahead buffering. This option
is enabled with ``CONFIG_NET_TCP_READAHEAD``. TCP read-ahead
buffer is necessary on TCP connections; otherwise data
received while there is no ``recv()`` in place would be lost.
For consistency, it would be best if such a TCP write
buffer implementation worked in a manner similar to the
existing TCP read-ahead buffering.
The following lists the NuttX configuration options
available to configure the TCP read-ahead buffering feature:
* ``CONFIG_NET_TCP_READAHEAD_BUFSIZE``: The size of one TCP read-ahead buffer.
* ``CONFIG_NET_NTCP_READAHEAD_BUFFERS``: The number of TCP
read-ahead buffers (may be zero to disable TCP/IP read-ahead buffering)
A future enhancement is to combine the TCP write buffer
management logic and the TCP read-ahead buffer management
so that one common pool of buffers can be used for both
functions (this would probably also require additional logic
to `throttle` read-buffering so that received messages do not
consume all of the buffers).

View file

@ -16,6 +16,8 @@ Network Support
netlink.rst
slip.rst
wqueuedeadlocks.rst
tcp_network_perf.rst
delay_act_and_tcp_perf.rst
``net`` Directory Structure ::

View file

@ -0,0 +1,193 @@
=======================
TCP Network Performance
=======================
.. warning::
Migrated from:
https://cwiki.apache.org/confluence/display/NUTTX/TCP+Network+Performance
(Abstracted and extended from a discussion from the NuttX Google group)
Question
========
For some unknown reason, I am seeing poor TCP network performance.
Answer
======
First let's talk about TCP send performance.
Source of Performance Bottlenecks
---------------------------------
General TCP send performance is not determined by the TCP stack as much
as it is by the network device driver. Bad network performance is due
to time lost `BETWEEN` packet transfers. The packet transfers themselves
go at the wire speed*. So if you want to improve performance on a
given network, you have to reduce time lost between transfers.
There is no other way.
Ignoring Ethernet issues like collisions, back-off delays,
inter-packet gaps (IPG), etc.
The time between packets is limited primarily by the buffering
design of the network driver. If you want to improve performance,
then you must improve the buffering at the network driver.
You need to support many full size (1500 byte) packet buffers.
You must be able to query the network for new data to transfer,
and queue those transfers in packet buffers. In order to reach
peak performance, the network driver must have the next transfer
buffered and ready-to-go before the previous transfer is finished
to minimize the GAP between packet transfers.
Different network devices also support more or less efficient
interfaces: The worst performing support interfaces that can
handle only one packet at a time, the best performing are able
to retain linked lists of packet buffers in memory and perform
scatter-gather DMA for a sequence of packets.
In the NuttX TCP stack, you can also improve performance by
enabling TCP write buffering. But the driver is the real key.
It would be good to have a real in-depth analysis of the
network stack performance to identify bottlenecks and
generate ideas for performance improvement. No one has
ever done that. If I were aware of any stack related
performance issue, I would certainly address it.
RFC 1122
--------
There is one important feature missing the NuttX TCP that
can help when there is no write buffering: Without write
buffering send() will not return until the transfer has
been ACKed by the recipient. But under RFC 1122, the host
need not ACK each packet immediately; the host may wait
for 500 MS before ACKing. This combination can cause very
slow performance when small, non-buffered transfers are
made to an RFC 1122 client. However, the RFC 1122 must
ACK at least every second (odd) packet so sequences of
packets with write buffering enabled do not suffer from
this problem.
`Update: RFC 1122 support was added to the NuttX TCP
stack with commit 66ef6d143a627738ad7f3ce1c065f9b1f3f303b0
in December of 2019. That, however, that affects only
received packet ACK behavior and has no impact on transmitted
packet performance; write buffering is still recommended.`
TCPBlaster
----------
I created a new test application at ``apps/examples/tcpblaster`` to
measure TCP performance and collected some data for the
configuration that happens to be on my desk. The `tcpblaster`
test gives you the read and write transfer rates in ``Kb/sec``
(I won't mention the numbers because I don't believe they
would translate any other setup and, hence, would be
misleading).
There is a nifty `TCP Throughput Tool <https://www.switch.ch/network/tools/tcp_throughput/>`_
that gives some theoretical upper limits on performance.
The tool needs to know the ``MSS`` (which is the Ethernet
packet size that you configured minus the size of the
Ethernet header, 14), the round-trip time (``RTT``)in
milliseconds (which you can
get from the Linux host ping), and a loss constant (which
I left at the default). With these values, I can determine
that the throughput for the NuttX TCP stack is approximately
at the theoretical limits. You should not be able to do
better any better than that (actually, it performs above
the theoretical limit, but I suppose that is why it is
"theoretical").
So, If you are unhappy with your network performance, the I
suggest you run the `tcpblaster` test, use that data
(along with the ``RTT`` from ping) with the
`TCP Throughput Tool <https://www.switch.ch/network/tools/tcp_throughput/>`_.
If you are still unhappy with the performance, don't go
immediately pointing fingers at the stack (which everyone does).
Instead, you should focus on optimizing your network
configuration settings and reviewing the buffer handling
of the Ethernet driver in you MCU.
If you do discover any significant performance issues
with the stack I will of course gladly help you resolve
them. Or if you have ideas for improved performance,
I would also be happy to hear those.
What about Receive Performance?
-------------------------------
All of the above discussion concerns `transmit performance`,
i.e., "How fast can we send data over the network?" The other
side is receive performance. Receive performance is very
different thing. In this case it is the remote peer who is
in complete control of the rate at which packets appear on
the network and, hence, responsible for all of the raw bit
transfer rates.
However, we might also redefine performance as the number of
bytes that were `successfully` transferred. In order for the
bytes to be successfully transferred they must be successfully
received and processed on the NuttX target. If we fail in
this if the packet is `lost` or `dropped`. A packet is lost if
the network driver is not prepared to receive the packet when
it was sent. A packet is dropped by the network if it is
received but could not be processed either because there
is some logical issue with the packet (not the case here)
or if we have no space to buffer the newly received packet.
If a TCP packet is lost or dropped, then the penalty is big:
The packet will not be ACKed, the remote peer may send a
few more out-of-sequence packets which will also be dropped.
Eventually, the remote peer will time out and retransmit
the data from the point of the lost packet.
There is logic in the TCP protocol to help manage these data
overruns. The TCP header includes a TCP `receive window` which
tells the remote peer how much data the receiver is able to
buffer. This value is sent in the ACK to each received
packet. If well tuned, this receive window could possibly
prevent packets from being lost due to the lack of
read-ahead storage. This is a little better. The remote
peer will hold off sending data instead of timing out and
re-transmitting. But this is still a loss of performance;
the gap between the transfer of packets caused by the hold-off
will result in a reduced transfer rate.
So the issues for good reception are buffering and processing
time. Buffering again applies to handling within the driver
but unlike the transmit performance, this is not typically
the bottleneck. And there is also a NuttX configuration
option that controls `read-ahead` buffering of TCP packets.
The buffering in the driver must be optimized to avoid lost
packets; the ` buffering can be tuned to minimize
the number packets dropped because we have no space to buffer them.
But the key to receive perform is management of processing
delays. Small processing delays can occur in the network
driver or in the TCP stack. But the major source of
processing delay is the application which is the ultimate
consumer of the incoming data. Imagine, for example,
and FTP application that is receiving a file over a
TCP and writing the file into FLASH memory. The primary
bottleneck here will be the write to FLASH memory which
is out of the control of software.
We obtain optimal receive performance when the processing
delays keep up with the rate of the incoming packets.
If the processing data rate is even slightly slower
then the receive data rate, then there will be a
growing `backlog` of buffered, incoming data to be
processed. If this backlog continues to grow then
eventually our ability to buffer data will be exhausted,
packets will be held off or dropped, and performance
will deteriorate. In an environment where a high-end,
remote peer is interacting with the low-end, embedded
system, that remote peer can easily overrun the
embedded system due to the embedded system's limited
buffering space, its much lower processing capability,
and its slower storage peripherals.

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View file

@ -0,0 +1,383 @@
============================
Framebuffer Character Driver
============================
.. warning::
Migrated from:
https://cwiki.apache.org/confluence/display/NUTTX/Framebuffer+Character+Driver
NX Graphics
===========
NuttX has supported higher level graphics for some time with
the OS's :doc:`/components/nxgraphics/index` and application
oriented :doc:`NxWidgets </applications/graphics/nxwidgets/index>` and the tiny
window manager :doc:`NxWM </applications/graphics/nxwm/index>`.
These are higher level in the sense that the primary
graphical function is to support windowing and control
of tools and toolbars within windows. These graphics
tools often do not meet the needs
of developers with very low end graphics and minimal display
requirements.
Figure 1
========
The framebuffer character driver, along with the option LCD
framebuffer interface, is an optional lighter-weight graphics interface.
.. image:: GraphicsInterfaces.png
Framebuffer Character Driver details
====================================
A `framebuffer character driver` has been recently been added
to bypass the complexity of `NX` and to provide a direct
application interface to the framebuffer graphic device.
The framebuffer buffer character devices, as with all
character devices, provides the interface to the graphics
device via stand POSIX VFS commands (``open()``, ``close()``,
``read()``, ``write()``, ``seek()``, ...), through IOCTL commands,
and for this driver via the ``mmap()`` function. These
interfaces are described below,
The framebuffer character driver is located in the NuttX
source tree at ``drivers/video/fb.c``. It is enabled in the
build with ``CONFIG_VIDEO_FB=y``. In order to register the
framebuffer driver, you will need to include logic in the
your board-specific start-up function that calls
``fb_register()`` That code sequence might look something
like:
.. code-block:: c
#include <nuttx/video/fb.h>
#ifdef CONFIG_VIDEO_FB
/* Initialize and register the simulated framebuffer driver */
ret = fb_register(0, 0);
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: fb_register() failed: %d\n", ret);
}
#endif
The ``fb_register()`` function takes two parameters:
* `display`. The display number for the case of boards
supporting multiple displays or for hardware that supports
multiple layers (each layer is consider a display). Typically zero.
* `plane`. Identifies the color plane on hardware that supports
separate framebuffer "planes" for each color component.
Should be zero because no planar hardware is currently
supported by NuttX.
``fb_register()`` will register the framebuffer character device
at ``/dev/fb`` `N` where `N` is the display number if the devices
supports only a single plane. If the hardware supports
multiple color planes, then the device will be registered
at ``/dev/fb`` `N-M` where `N` is the again display number but `M`
is the display plane.
There is a simple example at ``apps/examples/fb`` that provides
an illustration of most of the following interfacing methods.
POSIX Interfaces
================
The interaction with the framebuffer character driver via POSIX
VFS interface calls is the same as for other character drivers.
The only aspect that might require some additional discussion
is the use of ``read()``, ``write()``, and ``seek()``.
* ``read()`` returns data from the framebuffer memory and
updates the file position based on the number of bytes read.
* ``write()`` puts data into the framebuffer memory and
also updates the file position.
That file position is initially set to the position
zero meaning the beginning of the framebuffer. It is
advanced each time you ``read()`` from or ``write()`` to the
framebuffer. Is also updated by ``seek()``:
* ``seek()`` sets the file position to any desired
location within the framebuffer.
The file position is in units of `bytes`. This can be
confusing because other positional data may be in units
`pixels`. Pixels have different `depth` in different displays,
that is, different graphic hardware may support pixels with
differing bits-per-pixel. The pixel depth can be obtained
using one of the IOCTL commands listed below. Since the file
position is in bytes, the bits-per-pixel must be taken account
when using ``read()``, ``write()``, and ``seek()``. The usual conversion
from pixels to bytes is:
.. code-block:: C
start_byte = (start_pixel * bits_per_pixel) >> 3;
end_byte = (end_pixel * bits_per_pixel + 7) >> 3;
While the framebuffer may be accessed with these POSIX interfaces,
a more typical way of interacting with the framebuffer from an
application would involve use of ``mmap()`` as described below.
IOCTL Commands
==============
* ``FBIOGET_VIDEOINFO``. Get color plane info. Its argument is
pointer a writable instance of ``struct fb_videoinfo_s``:
.. code-block:: c
struct fb_videoinfo_s
{
uint8_t fmt; /* see FB_FMT_* */
fb_coord_t xres; /* Horizontal resolution in pixel columns */
fb_coord_t yres; /* Vertical resolution in pixel rows */
uint8_t nplanes; /* Number of color planes supported */
};
* ``FBIOGET_PLANEINFO``. Get video plane info. It received
a pointer to a writable instance of ``struct fb_planeinfo_s`` as its argument:
.. code-block:: C
struct fb_planeinfo_s
{
FAR void *fbmem; /* Start of frame buffer memory */
uint32_t fblen; /* Length of frame buffer memory in bytes */
fb_coord_t stride; /* Length of a line in bytes */
uint8_t display; /* Display number */
uint8_t bpp; /* Bits per pixel */
};
* ``FBIOGET_CMAP`` and ``FBIOPUT_CMAP``. Get/Put RGB color mapping.
These commands are available only if the hardware and
framebuffer driver support color mapping (``CONFIG_FB_CMAP=y``).
They each take a pointer to an instance of ``struct fb_cmap_s``
as an argument (writeable for ``FBIOGET_CMAP`` and read-only
for ``FBIOPUT_CMAP``).
.. code-block:: c
#ifdef CONFIG_FB_CMAP
struct fb_cmap_s
{
uint16_t first; /* Offset offset first color entry in tables */
uint16_t len; /* Number of color entries in tables */
/* Tables of color component. Any may be NULL if not used */
uint8_t *red; /* Table of 8-bit red values */
uint8_t *green; /* Table of 8-bit green values */
uint8_t *blue; /* Table of 8-bit blue values */
#ifdef CONFIG_FB_TRANSPARENCY
uint8_t *transp; /* Table of 8-bit transparency */
#endif
};
#endif
* ``FBIOGET_CURSOR``. Get cursor attributes. This command is
available only if the hardware and framebuffer driver
support cursors (``CONFIG_FB_HWCURSOR=y``). It take a pointer
to a writable instance of ``struct fb_cursorattrib_s``:
.. code-block:: c
#ifdef CONFIG_FB_HWCURSOR
#ifdef CONFIG_FB_HWCURSORIMAGE
struct fb_cursorimage_s
{
fb_coord_t width; /* Width of the cursor image in pixels */
fb_coord_t height /* Height of the cursor image in pixels */
const uint8_t *image; /* Pointer to image data */
};
#endif
struct fb_cursorpos_s
{
fb_coord_t x; /* X position in pixels */
fb_coord_t y; /* Y position in rows */
};
#ifdef CONFIG_FB_HWCURSORSIZE
struct fb_cursorsize_s
{
fb_coord_t h; /* Height in rows */
fb_coord_t w; /* Width in pixels */
};
#endif
struct fb_cursorattrib_s
{
#ifdef CONFIG_FB_HWCURSORIMAGE
uint8_t fmt; /* Video format of cursor */
#endif
struct fb_cursorpos_s pos; /* Current cursor position */
#ifdef CONFIG_FB_HWCURSORSIZE
struct fb_cursorsize_s mxsize; /* Maximum cursor size */
struct fb_cursorsize_s size; /* Current size */
#endif
};
#endif
* ``FBIOPUT_CURSOR``. Set cursor attributes. This command is
available only if the hardware and framebuffer driver
support cursors (``CONFIG_FB_HWCURSOR=y``). It take a
pointer to a writable instance of ``struct fb_setcursor_s``:
.. code-block:: c
#ifdef CONFIG_FB_HWCURSOR
struct fb_setcursor_s
{
uint8_t flags; /* See FB_CUR_* definitions */
struct fb_cursorpos_s pos; /* Cursor position */
#ifdef CONFIG_FB_HWCURSORSIZE
struct fb_cursorsize_s size; /* Cursor size */
#endif
#ifdef CONFIG_FB_HWCURSORIMAGE
struct fb_cursorimage_s img; /* Cursor image */
#endif
};
#endif
* ``FBIO_UPDATE``. This IOCTL command updates a rectangular region
in the framebuffer. Some hardware requires that there be
such a notification when a change is made to the
framebuffer (see, for example, the discussion of LCD drivers
below). This IOTCL command is if ``CONFIG_NX_UPDATE=y`` is
defined. It takes a pointer to a read-only instance of
``struct nxgl_rect_s`` that describes the region to be updated:
.. code-block:: c
struct nxgl_rect_s
{
struct nxgl_point_s pt1; /* Upper, left-hand corner */
struct nxgl_point_s pt2; /* Lower, right-hand corner */
};
``mmap()``
==========
Above we talked about using ``read()``, ``write()``, and ``seek()`` to
access the framebuffer. The simplest way to access the
framebuffer, however, is by using the ``mmap()`` to map
the framebuffer memory into the application memory
space. The following ``mmap()`` command, for example, can
be used to obtain a pointer to a read-able, write-able
copy of the framebuffer:
.. code-block:: c
FAR void *fbmem;
fbmem = mmap(NULL, fblen, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FILE, fd, 0);
if (state.fbmem == MAP_FAILED)
{
/* Handle failure */
...
}
printf("Mapped FB: %p\n", fbmem);
Where fd is the file descriptor of the opened framebuffer
character driver and ``fblen`` was obtained via an IOCTL
command as described above. NOTE that the framebuffer
buffer pointer is also available within the values
returned by the IOCTL commands. The address is a
kernel memory address and may not be valid in all
build configurations. Hence, ``mmap()`` is the preferred,
portable way to get the framebuffer address.
Framebuffer vs. LCD Graphics Drivers
====================================
Framebuffer graphics drivers are very common in high-end CPUs
but most low-end, embedded hardware will not support a
framebuffer.
A framebuffer graphics driver supports a region of memory
that is shared both by the software and by the graphics
hardware. Any modification to the framebuffer memory
results in a corresponding modification on the display
with no intervening software interaction. Some video
memory is dual ported to support concurrent video processor
and application processor accesses; or perhaps the LCD
peripheral just constantly DMAs the framebuffer memory
to the graphics hardware.
Most low-end embedded MCUs have a much simpler hardware
interface: The interface to the LCD may be through a simple
parallel interface or, more commonly, through a slower serial
interface such as SPI. In order to support such low-end
hardware with the framebuffer character driver, a special
software layer called the `Framebuffer LCD Front End` has
been developed. This is the topic of the next paragraph.
LCD Framebuffer Front-End
=========================
The `LCD Framebuffer Front-End` provides a standard NuttX
framebuffer interface, but works on top of a standard
parallel or serial LCD driver. It provides the framebuffer,
the framebuffer interface, and the hooks to adapt the LCD
driver. The LCD framebuffer front-end can be found in the
NuttX source tree at ``drivers/lcd/lcd_framebuffer.c``.
In order to provide updates to the LCD hardware after
updates to the framebuffer, the LCD framebuffer front-end
must be notified when significant changes to the framebuffer
have been made. This notification is supported when
``CONFIG_NX_UPDATE=y`` is defined in the configuration. In
this case, the LCD framebuffer front-end will support
the special. OS-internal interface function ``nx_notify_rectangle()``
which defines the rectangular region in the framebuffer that
has been changed. In response to a call to ``nx_notify_rectangle()``
will use the lower-level LCD interface to update only that
rectangular region on the display.
This kind of update for standard LCD drivers is very efficient:
It is usually more efficient to update a region on the
display than it is for form a complex image with text and
line drawing; the updated region seems to update very
quickly because of that. In fact, many of the low-end
LCD drivers already include an internal framebuffer to
support this style of LCD update.
When used with LCD character driver, the ``nx_notify_rectangle()``
function will be called by the character river in response
to the ``FBIO_UPDATE IOCTL`` command.
Another advantage of the framebuffer, both the LCD internal
framebuffer and the framebuffer character driver, is
that super-efficient reading of the LCD display memory:
The LCD display memory is not read at all! The read is
from the copy in the framebuffer.
Of course, using both an LCD internal framebuffer with the
framebuffer character drivers is wasteful; one framebuffer
is enough!
As a caution, it is important to remember that a framebuffer
can be quite large. For example, a 480x320 display with
16-bit RGB pixels would require an allocated framebuffer
of size 300 KiB. This is inappropriate with most small
MCUs (unless they support external memory). For tiny displays,
such as 128x64 1-bit monochromatic displays, the framebuffer
memory usage is not bad: 1 KiB in that example.
Framebuffer Graphics Library
============================
Now the missing part is some kind of application-space
framebuffer graphics library. The NuttX framebuffer
driver is superficially similar to the Linux framebuffer
driver so there is a lot of support for Linux
framebuffer graphics support that should be easily ported to
NuttX Perhaps DirectFB would be an GPL option? SDL with its
MIT license might be a more compatible source for such a port.

View file

@ -171,6 +171,8 @@ NX Header Files
nxtk.rst
nxfonts.rst
nxcursor.rst
nxwm_threading.rst
framebuffer_char_driver.rst
sample.rst
appendix.rst

View file

@ -0,0 +1,13 @@
==============
NxWM Threading
==============
.. warning::
Migrated from:
https://cwiki.apache.org/confluence/display/NUTTX/NxWM+Threading
.. image:: nxwm_threading_model/nxwm_theading_model_page_0.png
.. image:: nxwm_threading_model/nxwm_theading_model_page_1.png
.. image:: nxwm_threading_model/nxwm_theading_model_page_2.png
.. image:: nxwm_threading_model/nxwm_theading_model_page_3.png
.. image:: nxwm_threading_model/nxwm_theading_model_page_4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB