add sim and drivers guides, contributing workflow

This commit is contained in:
Adam Feuer 2020-08-30 17:48:30 -07:00 committed by Matias N
parent 41f432eb18
commit 56b89a89f2
6 changed files with 618 additions and 0 deletions

View File

@ -1,4 +1,7 @@
=================
.. include:: /substitutions.rst
.. _coding-standard:
C Coding Standard
=================

View File

@ -8,6 +8,7 @@ and documentation (the one you are reading now):
:maxdepth: 2
workflow.rst
making-changes.rst
coding_style.rst
documentation.rst

View File

@ -0,0 +1,256 @@
.. include:: /substitutions.rst
.. _making-changes:
Making Changes
==============
If you want to make changes to NuttX, for your own personal use, or to submit them back to project to improve NuttX,
that's easy. For the purposes of this guide, you'll need a `GitHub <https://www.github.com>`_ account, since
the Apache NuttX team uses GitHub. (You could also use git locally, or save your changes to other sites like
`GitLab <https://about.gitlab.com/>`_ or `BitBucket <https://bitbucket.org>`_, but that's beyond the scope of this
guide).
Here's how to do it:
#. Set your git user name and email
.. code-block:: bash
$ cd nuttx/
$ git config --global user.name "Your Name"
$ git config --global user.email "yourname@somedomaincom"
#. Sign in to GitHub
If you don't have a `GitHub <https://www.github.com>`_ account, it's free to
sign up.
|br|
|br|
#. Fork the Project
Visit both these links and hit the Fork button in the upper right of the page:
* `NuttX <https://github.com/apache/incubator-nuttx/>`_
* `NuttX Apps <https://github.com/apache/incubator-nuttx-apps/>`_
#. Change the Git Remotes
The git repositories in your project are currently connected to the official NuttX repositories, but you don't
have permission to push software there. But you can push them to your forks, and from there create Pull Requests
if you want to send them to the NuttX project.
First, remove the current remote, ``origin`` (we'll add it back later):
.. code-block:: bash
$ cd nuttx/
$ # display the remote
$ git remote -v
You should see something like this:
.. code-block:: bash
origin https://github.com/apache/incubator-nuttx.git
Now, on the GitHub web page for your forked ``incubator-nuttx`` project, copy the clone url get it by hitting the
green ``Clone or Download`` button in the upper right. Then do this:
.. code-block:: bash
$ git remote rm origin
$ git remote add origin <your forked incubator-nuttx project clone url>
$ git remote add upstream https://github.com/apache/incubator-nuttx.git
Do the same for your forked ``incubator-nuttx-apps`` project:
.. code-block:: bash
$ cd ../apps
$ git remote rm origin
$ git remote add origin <your forked incubator-nuttx-apps project clone url>
$ git remote add upstream https://github.com/apache/incubator-nuttx-apps.git
#. Create a Local Git Branch
Now you can create local git branches and push them to GitHub:
.. code-block:: bash
$ git checkout -b test/my-new-branch
$ git push
Git Workflow With an Upstream Repository
----------------------------------------
The main NuttX git repository is called an "upstream" repository - this is because it's the main source of truth, and
its changes flow downstream to people who've forked that repository, like us.
Working with an upstream repo is a bit more complex, but it's worth it since you can submit fixes and features
to the main NuttX repos. One of the things you need to do regularly is keep your local repo in sync
with the upstream. I work with a local branch, make changes, pull new software from the upstream and merge it in,
maybe doing that several times. Then when everything works, I get my branch ready to do a Pull Request. Here's how:
#. Fetch upstream changes and merge into my local master:
.. code-block:: bash
$ git checkout master
$ git fetch upstream
$ git merge upstream/master
$ git push
#. Merge my local master with my local branch:
.. code-block:: bash
$ git checkout my-local-branch
$ git merge master
$ git push
#. Make changes and push them to my fork
.. code-block:: bash
$ vim my-file.c
$ git add my-file.c
$ git commit my-file.c
$ git push
#. Repeat 1-3 as necessary
#. Run the ``tools/checkpatch.sh`` script on your files
When your code runs, then you're almost ready to submit it. But first you need to check the code to ensure
that it conforms to the NuttX :ref:`contributing/coding_style:C Coding Standard`.
The ``tools/checkpatch.sh`` script will do that. Here's the usage info:
.. code-block:: bash
$ ./tools/checkpatch.sh -h
USAGE: ./tools/checkpatch.sh [options] [list|-]
Options:
-h
-c spell check with codespell(install with: pip install codespell
-r range check only (used with -p and -g)
-p <patch list> (default)
-g <commit list>
-f <file list>
- read standard input mainly used by git pre-commit hook as below:
git diff --cached | ./tools/checkpatch.sh -
Run it against your files and correct all the errors in the code you added, so that
``tools/checkpatch.sh`` reports no errors. Then commit the result.
For example:
.. code-block:: bash
$ ./tools/checkpatch.sh -f my-file.c
arch/arm/src/sama5/hardware/my-file.c:876:82: warning: Long line found
$ # fix errors
$ vim my-file.c
$ # run again
$ ./tools/checkpatch.sh -f my-file.c
If you have made a lot of changes, you can also use this bash commandline to see the errors for all the changed C
files in your branch (assumes you are currently on the branch that has the changed files):
.. code-block:: bash
$ git diff --name-only master | egrep "\.c|\.h" | xargs echo | xargs ./tools/checkpatch.sh -f | less
Note that there are some bugs in the ``nxstyle`` program that ``checkpatch.sh`` uses, so
it may report a few errors that are not actually errors. The committers will help you
find these. (Or view the
`nxstyle Issues <https://github.com/apache/incubator-nuttx/issues?q=is%3Aissue+is%3Aopen+nxstyle>`_.)
|br|
|br|
#. Commit the fixed files
.. code-block:: bash
$ git add my-file.c
$ git commit my-file.c
$ git push
Submitting Your Changes to NuttX
--------------------------------
Pull requests let you tell others about changes you've pushed to a branch in a repository on GitHub. Once a pull
request is opened, you can discuss and review the potential changes with collaborators and add follow-up commits
before your changes are merged into the base branch.
(from GitHub's `About pull requests <https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests>`_ page)
Before you do a Pull Request, the NuttX team will usually want all the changes you made in your branch "squashed" into
a single commit, so that when they review your changes, there's a clean view of the history. If there are changes
after Pull Request review feedback, they can be separate commits. Here's the easiest way I found to do that initial
squash before submitting the Pull Request:
#. Check out my branch
.. code-block:: bash
$ git checkout my-branch
#. Rename it to ``my-branch-old`` to save it, because we're going to create new clean branch to push:
.. code-block:: bash
$ git branch -m my-branch-old
#. Create a new clean branch with the same name you were using before the last step:
.. code-block:: bash
$ git checkout master
$ git checkout -b my-branch
#. Merge your saved old branch into the new one, telling git to "squash" all your commits into one (note this will
not commit the result; the changed files will be in your staging area, ready to be committed):
.. code-block:: bash
$ git merge --squash my-branch-old
#. Commit the result
.. code-block:: bash
$ git commit
#. Force-push your new clean branch to the remote— this will overwrite all your previous changes in that branch:
.. code-block:: bash
$ git push -f --set-upstream origin my-branch
#. Create a GitHub Pull Request
A Pull Request is how you ask your upstream to review and merge your changes.
Here's `GitHub's instructions for creating a Pull Request <https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request>`_.
|br|
#. Get Pull Request feedback and implement changes
Get suggestions for improvements from reviewers, make changes, and push them to the branch. Once the reviewers are
happy, they may suggest squashing and merging again to make a single commit. In this case you would repeat steps
1 through 6.
Git Resources
-------------
* `Git Cheat Sheet (by GitHub) <https://github.github.com/training-kit/downloads/github-git-cheat-sheet.pdf>`_
* `Git Book (online) <https://git-scm.com/book/en/v2>`_
* `NuttX Code Contribution Workflow <https://cwiki.apache.org/confluence/display/NUTTX/Code+Contribution+Workflow>`_
All the details are here if you need them!

View File

@ -0,0 +1,211 @@
.. include:: /substitutions.rst
.. _drivers:
Drivers
=======
Some NuttX boards don't have full support for all the on-chip peripherals. If you need support for this hardware,
you will either need to port a driver from another chip, or write one yourself. This section discusses how to do that.
Porting a Driver
----------------
Often support for on-chip peripherals exists in a closely related chip, or even a different family or from a different
manufacturer. Many chips are made up of different Intellectual Property (IP) blocks that are licensed from vendors like
Cadence, Synopsys, and others. The IP blocks may be similar enough to use another chip's driver with little
modification.
* Find a similar driver in NuttX source code:
* Look at register names listed in the datasheet for the peripheral.
* Search the NuttX codebase for the register names (try several different ones).
* Note that you'll have to compare the datasheet to the header and code files to see if there are differences; there
will usually be some differences between architectures, and they can be significant.
* Find a similar driver in U-Boot source code:
* Only for inspiration, you can't copy code because of license incompatibility.
* But you can debug to see how the driver works.
* `U-Boot <https://www.denx.de/wiki/U-Boot>`_ drivers are often easier to understand than BSD Unix drivers because
U-Boot is simpler.
* Find a similar driver in Zephyr or BSD Unix (OpenBSD, FreeBSD, NetBSD):
* If you find one, you can borrow code directly (Apache 2.0 and BSD licenses are compatible).
* Understanding how the driver works
Here are a couple of techniques that helped me.
* printf debugging
* Sprinkle ``custinfo()`` logging statements through your code to see execution paths and look at variables
while the code is running. The reason to use ``custinfo()`` as opposed to the other logging shortcuts
(``mcinfo()``, etc.) is that you can turn on and off other other logging and still see your custom debug
logging. Sometimes it's useful to quiet the flood of logging that comes from a particular debug logging
shortcut.
* Note that printing info to the console will affect timing.
* Keep a file with just your debug settings in it, like this (``debugsettings``):
.. code-block:: c
CONFIG_DEBUG_CUSTOM_INFO=y
(etc..)
* Add the settings to the end of your ``.config`` file after running ``make menuconfig`` (that will reorder
the file, making it harder to see and change the debug settings if you need to).
.. code-block:: bash
$ cat .config debugsettings > .config1 ; mv .config1 .config
* If you are using interrupts and threads (many things in NuttX run in different threads as a response to interrupts),
you can use printf debugging to see overlapping execution.
* Interrupts - here's how to inspect the C stack frame to see what execution environment is currently running:
.. code-block:: c
uint32_t frame = 0; /* MUST be the very first thing in the function */
uint32_t p_frame;
frame++; /* make sure that frame doesn't get optimized out */
p_frame = (uint32_t)(&frame);
custinfo("p_frame: %08x\n", p_frame);
* Threads - here's how to get the thread identifier to see which thread is currently executing:
.. code-block:: c
pthread_t thread_id = pthread_self();
custinfo("pthread_id: %08x\n", thread_id);
* stack frame printf
* thread printf
* `GDB — the Gnu Debugger <https://www.gnu.org/software/gdb/>`_
GDB is a great tool. In this guide we've already used it to load our program and run it. But it can do a lot
more. You can single-step through code, examine variables and memory, set breakpoints, and more. I generally use
it from the commandline, but have also used it from an IDE like JetBrains' Clion, where it's easier to see the
code context.
One add-on that I found to be essential is the ability to examine blocks of memory, like buffers that NuttX uses
for reading and writing to storage media or network adapters. This `Stack Overflow question on using GDB to
examine memory <https://stackoverflow.com/a/54784260/431222>`_ includes a GDB command that is very handy. Add
this to your ``.gdbinit`` file, and then use the ``xxd`` command to dump memory in an easy-to-read format:
.. code-block::
define xxd
if $argc < 2
set $size = sizeof(*$arg0)
else
set $size = $arg1
end
dump binary memory dump.bin $arg0 ((void *)$arg0)+$size
eval "shell xxd -o %d dump.bin; rm dump.bin", ((void *)$arg0)
end
document xxd
Dump memory with xxd command (keep the address as offset)
xxd addr [size]
addr -- expression resolvable as an address
size -- size (in byte) of memory to dump
sizeof(*addr) is used by default end
Here's a short GDB session that shows what this looks like in practice. Note that the memory location being
examined (``0x200aa9eo`` here) is a buffer being passed to ``mmcsd_readsingle``:
.. code-block::
Program received signal SIGTRAP, Trace/breakpoint trap.
0x200166e8 in up_idle () at common/arm_idle.c:78
78 }
(gdb) b mmcsd_readsingle
Breakpoint 1 at 0x2006ea70: file mmcsd/mmcsd_sdio.c, line 1371.
(gdb) c
Continuing.
Breakpoint 1, mmcsd_readsingle (priv=0x200aa8c0, buffer=0x200aa9e0 "WRTEST TXT \030", startblock=2432) at mmcsd/mmcsd_sdio.c:1371
1371 finfo("startblock=%d\n", startblock);
(gdb) xxd 0x200aa9e0 200
200aa9e0: 5752 5445 5354 2020 5458 5420 1800 0000 WRTEST TXT ....
200aa9f0: 0000 0000 0000 0000 0000 5500 1100 0000 ..........U.....
200aaa00: 5752 5445 5354 3520 5458 5420 1800 0000 WRTEST5 TXT ....
200aaa10: 0000 0000 0000 0000 0000 5800 1500 0000 ..........X.....
200aaa20: e552 5445 5854 3620 5458 5420 1800 0000 .RTEXT6 TXT ....
200aaa30: 0000 0000 0000 0000 0000 5600 1200 0000 ..........V.....
200aaa40: 5752 5445 5354 3620 5458 5420 1800 0000 WRTEST6 TXT ....
200aaa50: 0000 0000 0000 0000 0000 5600 1200 0000 ..........V.....
200aaa60: 0000 0000 0000 0000 0000 0000 0000 0000 ................
200aaa70: 0000 0000 0000 0000 0000 0000 0000 0000 ................
200aaa80: 0000 0000 0000 0000 0000 0000 0000 0000 ................
200aaa90: 0000 0000 0000 0000 0000 0000 0000 0000 ................
200aaaa0: 0000 0000 0000 0000 ........
NuttX Drivers as a Reference
----------------------------
If you're not porting a NuttX driver from another architecture, it still helps to look at other similar NuttX
drivers, if there are any. For instance, when implementing an Ethernet driver, look at other NuttX Ethernet drivers;
for an SD Card driver, look at other NuttX Ethernet drivers. Even if the chip-specific code won't be the same, the
structure to interface with NuttX can be used.
Using Chip Datasheets
---------------------
To port or write a driver, you'll have to be familiar with the information in the chip datasheet. Definitely find
the datasheet for your chip, and read the sections relevant to the peripheral you're working with. Doing so ahead
of time will save a lot of time later.
Another thing that's often helpful is to refer to sample code provided by the manufacturer, or driver code from
another operating system (like U-Boot, Zephyr, or FreeBSD) while referring to the datasheet — seeing how working
code implements the necessary algorithms often helps one understand how the driver needs to work.
* How to use a datasheet
Key pieces of information in System-on-a-Chip (SoC) datasheets are usually:
* Chip Architecture Diagram — shows how the subsections of the chip (CPU, system bus, peripherals, I/O, etc.) connect
to each other.
* Memory Map — showing the location of peripheral registers in memory. This info usually goes into a header file.
* DMA Engine — if Direct Memory Access (DMA) is used, this may have info on how to use it.
* Peripheral — the datasheet usually has a section on how the peripheral works. Key parts of this include:
* Registers List — name and offset from the base memory address of the peripheral. This needs to go into a header
file.
* Register Map — what is the size of each register, and what do the bits mean? You will need to create ``#defines``
in a header file that your code will use to operate on the registers. Refer to other driver header files for
examples.
`Logic analyzers <https://en.wikipedia.org/wiki/Logic_analyzer>`_
-----------------------------------------------------------------
For drivers that involve input and output (I/O), especially that involve complex protocols like SD Cards, SPI, I2C,
etc., actually seeing the waveform that goes in and out the chip's pins is extremely helpful. Logic analyzers can
capture that information and display it graphically, allowing you to see if the driver is doing the right thing
on the wire.
DMA Debugging
-------------
* Dump registers before, during, and after transfer. Some NuttX drivers (``sam_sdmmc.c`` or ``imxrt_sdmmc.c`` for
example) have built-in code for debugging register states, and can sample registers before, during, and
immediately after a DMA transfer, as well as code that can dump the peripheral registers in a nicely-formatted
way onto the console device (which can be a serial console, a network console, or memory). Consider using something
like this to see what's happening inside the chip if you're trying to debug DMA transfer code.
* Compare register settings to expected settings determined from the datasheet or from dumping registers from working
code in another operating system (U-Boot, Zephyr, FreeBSD, etc.).
* Use the ``xxd`` GDB tool mentioned above to dump NuttX memory buffers before, during, and after a transfer to see if
data is being transferred correctly, if there are over- or under-runs, or to diagnose data being stored in incorrect
locations.
* printf debugging register states can also help here.
* Remember that logging can change the timing of any algorithms you might be using, so things may start or stop
working when logging is added or removed. Definitely test with logging disabled.

View File

@ -7,3 +7,5 @@ Guides
.. toctree::
nfs.rst
usbtrace.rst
simulator.rst
drivers.rst

View File

@ -0,0 +1,145 @@
.. include:: /substitutions.rst
.. _simulator:
Simulator
=========
Apache NuttX has a simulator that can run as a regular program on Linux, Mac, and Windows computers.
It's useful for debugging operating system features that aren't associated with particular
device drivers— for instance the TCP/IP stack itself, a web interface or API for your
application, or other communication protocols. It's also handy for trying out Apache NuttX without
having a piece of embedded hardware.
This guide assumes you're on Linux. It works on Windows and Mac too— if you know how,
submit a PR the NuttX Companion to update this guide!
Compiling
---------
#. Configure the Simulator
There are a lot of simulator configurations available that set you up to test various
operating system features.
Here we'll use the ``sim:tcpblaster`` configuration because it comes with networking
that is ready to use.
.. code-block:: bash
$ cd nuttx
$ ./tools/configure.sh sim:tcpblaster
#. Compile
.. code-block:: bash
$ make clean; make
Running
-------
#. Give the Simulator Privileges
On recent Linux distributions, you need to give the ``nuttx`` program the capabilities
(similar to permissions) to access the network:
.. code-block:: bash
$ sudo setcap cap_net_admin+ep ./nuttx
#. Run the simulator:
.. code-block:: bash
$ ./nuttx
#. Bring Up the Network Interfaces
On Apache NuttX:
.. code-block:: bash
nsh> ifup eth0
On Linux, first you need to find your main network interface— this will usually either
be an ethernet or wireless network adapter. Do this:
.. code-block:: bash
$ ifconfig
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 5846 bytes 614351 (614.3 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 5846 bytes 614351 (614.3 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
wlp0s20f3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.209 netmask 255.255.255.0 broadcast 192.168.1.255
inet6 fe80::1161:c26b:af05:d784 prefixlen 64 scopeid 0x20<link>
ether 24:41:8c:a8:30:d1 txqueuelen 1000 (Ethernet)
RX packets 219369 bytes 176416490 (176.4 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 108399 bytes 27213617 (27.2 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
``lo0`` is the Loopback Interface, so ``wlp0s20f3`` is the wireless interface. Note
that it has an IP address on the local net. There may be other interfaces listed, you'll
need to pick the one that's right for your system.
Then, on Linux do this to set up the tap network interface and route that will let
the Apache Nuttx simulator access the network:
.. code-block:: bash
$ sudo ./tools/simhostroute.sh wlp0s20f3 on
$ ping -c 1 10.0.1.2 # nuttx system
PING 10.0.1.2 (10.0.1.2) 56(84) bytes of data.
64 bytes from 10.0.1.2: icmp_seq=1 ttl=64 time=7.52 ms
--- 10.0.1.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 7.529/7.529/7.529/0.000 m
#. Test that Apache NuttX can access the Internet
First let's ping the network interface of our Linux host to prove we can see the
gateway to the Internet:
.. code-block:: bash
nsh> ping -c 1 10.0.1.1
nsh> ping -c 1 10.0.1.1
PING 10.0.1.1 56 bytes of data
56 bytes from 10.0.1.1: icmp_seq=0 time=0 ms
1 packets transmitted, 1 received, 0% packet loss, time 1010 ms
Now let's ping one of Google's DNS servers to prove we can access the rest of the
Internet:
.. code-block:: bash
nsh> ping -c 1 8.8.8.8
PING 8.8.8.8 56 bytes of data
56 bytes from 8.8.8.8: icmp_seq=0 time=10 ms
1 packets transmitted, 1 received, 0% packet loss, time 1010 ms
Success!
Stopping
--------
The only really effective way to stop the simulator is kill it from another terminal:
.. code-block:: bash
$ pkill nuttx
Debugging
---------
You can debug the simulator like any regular Linux program.