0xFFFFFFFE, reclaim_reent() error symptoms of embedded programming

This is a report of one cryptic symptom (and possible) fixes you might encounter when you are embedded programming.  I report it because I have experienced it more than once and always forget what it means.

When you are trying to flash your embedded mcu,  the debugger seems to download to the chip, the debugger starts  and then stops showing a stack trace something like this:

0xFFFFFFFE
reclaim_reent()

Usually you expect the debugger to stop at main() and wait for you to tell the debugger to run (but that depends on whether you have configured your IDE and debugger to breakpoint at main.)

It might mean (not a program bug, a process error):

  • your linker script <foo>.ld describes the memory of your chip incorrectly
  • you haven’t erased the chip’s ROM yet

About the latter.  I am not sure, but modules you buy might already be flashed with a program such as a DFU bootloader, and are configured to protect a debugger from overwriting that code in ROM.  For example, on the Nordic NRF51, to remove the protection and erase all of ROM so that you can then use the debugger:

 nrfjprog --recover --family NRF51

 

 

Advertisements

Vagga Rust embedded

TL;DR

Work in progress: so far, a vagga container of Rust tools.  Eventually in the container, tools for embedded Rust (Xargo) and my own source project under version control.

A similar endeavour is Japaric’s “cross”.   The differences:

  • that uses a Docker container, here I use Vagga container
  • that might be ready to use, this is an explanatory exploration

A repository of source for this blog.

About

This is:

  • a log of my experience
  • well linked
  • for audience: developers/programmers.

Background

I have been programming embedded computers in C++.   I hate C++.  I have also used Python and Swift.  I read some background material:

So here I try to install Rust.  Ultimately I want to program in Rust an embedded ARM mcu on a NRF52 radio chip using the NRF52DK dev board.

Meta

Typically a developer knows/remembers how to set up a development machine (how to install the OS, development packages, an IDE, etc.)  Typically, you use the GUI, maybe write a shell script, take good notes, iterate when you discover packages missing.

Vagga helps you capture the entire process of setting up a development machine.  You capture the process in a vagga configuration file.  Which is a text configuration file or script; no GUI.

This blog itself is an annotated record of writing and debugging such a vagga configuration file.

Strategy

The Rust project moves fast.  I don’t want to struggle with keeping up to date.  I am not sure I will keep it.  So I will install Rust in a container.  A container is like a virtual machine, but lighter weight, and only on Linux.

Vagga github repository

Vagga implements containers.  Vagga is targeted for developers i.e. specialized to contain development environments. It seems like a natural fit.

Advantages of containers/virtual machines:

  • throwaway , non-invasive:  Can’t destroy your computer’s installation of non-development packages, your personal applications such as Gimp, LibreOffice )
  • distributable: you can give a container to other developers

Advantages of vagga:

  • is platform (Linux distribution) agnostic.  Vagga scripts might be portable to other developer’s machines (Linux-like.)
  • is a high-level package manager
  • userspace (doesn’t require root privileges)

Vagga is written in Rust.  (I hoped Vagga might even install Rust for me  but no, although written in Rust, I install a binary Vagga, which means I need to install Rust separately, but in a container.)  In this case, using Vagga is a form of “eating your own dog food”: if you are going to learn Rust, you might as well use tools that are written in Rust.

More meta

Vagga is a high-level package manager.  (discusses goals and future.)

Rustup is also a package manager (toolchain manager) exclusively for Rust.

Xargo is also a toolchain manager, exclusively for cross-compiling Rust language programs.

So it seems strange that to combat the proliferation of package managers, we invent yet another higher-level package manager.   And here we use a chain/graph of package managers:  vagga, rustup, cargo, xargo, your favorite Linux distribution’s package manager.

A high-level package manager makes more sense if you are targeting your app to many Linux distributions.  Here, I am only targeting (ultimately) one embedded architecture.  But by using a high-level package manager, I can distribute my development environment.  And I can easily replicate my home dev machine in other remote physical locations.

Installing Vagga

Vagga instructions for installation.

Per the above, on Ubuntu, just paste this into a terminal:

echo 'deb [arch=amd64 trusted=yes] https://ubuntu.zerogw.com vagga main' | sudo tee /etc/apt/sources.list.d/vagga.list
sudo apt-get update
sudo apt-get install vagga

(That adds a repository for vagga, and installs vagga from it.)

Putting an OS in my container

Vagga docs on configuration

Vagga is configured from a text file, vagga.yaml.

I created this simple directory tree:

rustdev/
    vagga.yaml

With the contents of vagga.yaml:

containers:
  rustdev:
    setup:
    - !Ubuntu yakkety
    
commands:
  test: !Command
    description: Test 
    container: rustdev
    run: [ps]
 

In other words, a container named “rustdev” and a command “test”.

Testing Vagga

At at terminal, change directory to “rustdev” and enter “vagga test”.  I got (unexpected):

(1/1) Installing alpine-keys (1.3-r0)
OK: 0 MiB in 1 packages
fetch http://repos.mia.lax-noc.com/alpine/v3.5/main/x86_64/APKINDEX.tar.gz
ERROR: http://repos.mia.lax-noc.com/alpine/v3.5/main: No such file or directory

Which seems like a problem with online repositories.  I entered “sudo apt-get update”, and tried again.  This time vagga seemed to install an OS in the container (in about a minute) and run the command, yielding:

PID TTY TIME CMD
 1 ? 00:00:00 exe
 2 ? 00:00:00 ps

IOW, there are only a few processes running in the container.

Emphasizing:  in my experience I had to do this to get it to work:

cd rustDev
vagga test    (fails to get alpine-keys)
sudo apt-get update
vagga test

Installing Rust compiler in the container

From my reading, I know the rust compiler command is “rustc”.  Entering that at a command line, expect:

The program 'rustc' is currently not installed. You can install it by typing:
sudo apt install rustc

So I know that ubuntu has a package.  I don’t want it installed directly, but in the vagga container.

Make this change in vagga.yaml:

    - !Ubuntu yakkety
    - !Install [rustc]

IOW, tell vagga you want to install the package named “rustc” in the container.

Now  “vagga test” first checks the container configuration for updates and yields:

Generating locales (this might take a while)...
 en_US.UTF-8... done
Generation complete.
Reading package lists... Done
E: Method mirror has died unexpectedly!
E: Sub-process mirror received a segmentation fault.
WARN:vagga::builder::commands::ubuntu: The `apt-get update` failed. You have no mirror setup, and default one is not always perfect.
Add the following to your ~/.vagga.yaml:
 ubuntu-mirror: http://CC.archive.ubuntu.com/ubuntu
Where CC is a two-letter country code where you currently are.

So I created a file named “.vagga.yaml” in my home directory (this is a hidden “settings” file.  Do not change your yagga configuration file i.e. ~/rustdev/vagga.yaml) with the contents

ubuntu-mirror: http://us.archive.ubuntu.com/ubuntu

Now, “vagga test” yields:

...
E: Failed to fetch http://security....
...
E: Some index files failed to download. They have been ignored, or old ones used instead.
WARN:vagga::builder::commands::ubuntu: The `apt-get update` failed. If this happens too often, consider changing the `ubuntu-mirror` in settings

So I followed this thread to find the “best” mirror, and changed the mirror, but it still fails.

So now I rethink: I really want embedded Rust, which suggests the nightly build of Rust, not the outdated package that Ubuntu provides.  I don’t want to install packaged Rust, I want to install rustup….

Installing rustup

Rust install instructions.

Rust is usually installed by the “rustup” tool.  New goal: install rustup in the container.  It seems like Ubuntu does not package rustup separately.  So edit vagga.yaml to add the instructions given by Rust.org for installing rustup, wrapped in a shell inside vagga.   Neively:

    - !Ubuntu yakkety
    - !Sh "curl https://sh.rustup.rs -sSf | sh"

But those instructions download a shell script and pipe it to a shell and the shell script is interactive.  So I hacked some more.  Summarizing the struggle:

  • curl was absent from the container
  • the curl package would not install because of mirrors outdated
  • I switched OS version to Xenial (Ubuntu 16.04LTS) hoping the mirrors were more stable
  • I switched to wget instead of curl
  • the rustup shell script requires curl

Now I read vagga examples.  From github:vagga-examples/python  I found:

  • Most developers install an omnibus package “build-essential” that includes all the commands a developer typically uses.  IOW,  a bare OS unsuited is for developers.
  • vagga has its own construct for downloading files

And the yagga configuration file for yagga itself builds a Rust development environment (since vagga is written in Rust.)

Using those examples, I ended up with the script which you can find in my git repository.  I don’t include the script here, it may suffer revisions.

If you enter “vagga test” expect:

/work/.home/.cargo/bin/rustup
/work/.home/.cargo/bin/rustc
rustc 1.15.1 (021bd294c 2017-02-08)

Now I wondering whether I can run my IDE in the container, and how my source code gets into and out of the container (probably git.)  The answer seems to be that the directory where you invoke vagga is the “project” directory and is mapped into the container as /work.  Your IDE can work outside the container.  All artifacts of the build should be in the container and not pollute your project directory?

Brief notes

Create a hidden setup or options file ~/.vagga.yaml with contents: ubuntu-mirror: http://us.archive.ubuntu.com/ubuntu

Until you get your vagga.yml correct, vagga seems to repeatedly download dependency packages.  IOW, errors prevent completion of the container.  When you achieve a correct container, then vagga knows, and only downloads dependency packages as needed (when the repository publishes a security update or a nightly update?  Commands don’t establish dependencies?)

Doing “sudo apt-get update” between iterations seems to help some errors.

The directory where you invoke vagga is the “project” directory and is mapped into the container as /work.

Vagga stores the container in the hidden file .vagga in the project directory (alongside your vagga.yaml.)  (To delete a container?)

Continue with Part Two…

 

 

Using relative coordinates in KiCad to design mechanical aspects of PCB boards

TL;DR: press the space bar to set the origin of the relative coordinate system and then watch the dx,dy cursor coordinates in the status bar as you draw.

See  section 2.6. “Displaying cursor coordinates” of the Eeschema reference manual.

There are two coordinates systems (frames) in KiCad:

  • absolute: origin is in one of the corners of the “paper” sheet, displays as “X…Y…”
  • relative: origin is wherever you set it using the space bar, displays as “dx…dy….”

KiCad continually displays the location of the cursor in the right side of the status bar which appears near the bottom of the application window.  KiCad updates the displayed location even as you use some tool to draw, place, etc.  KiCad displays the location of the cursor in both coordinate systems.

Use the relative coordinate system to layout a board mechanically.  First set the origin, say to the upper left corner of your board:

  • move the cursor to where you want it
  • press the space bar.  Expect the relative coordinates to change to “dx 0.0000 dy 0.0000.”

Then as you draw, you can stop the cursor at some precise dimension.

KiCad does not persist the origin of the relative coordinate system (save it in your project.)  You need to set the origin at the beginning of each design session.

KiCad does not display any particular symbol at the origin of the relative coordinate system.  You can add a fiducial symbol at the origin.

Few people use the absolute coordinate system and many people complain that you can’t set its origin.  But they should just use the relative coordinate system.

From a user-interface viewpoint, maybe KiCad should:

  • place more emphasis on the relative coordinate system (display relative coords left of/preceding the absolute coords)
  • make the origin persist
  • add a pop-up menu item to set the origin (space bar is too obscure)
  • make the displayed nomenclature more consistent (why is it not “dX,dY and dx,dy” or “X,Y and x,y” or “aX, aY and rX, rY” or “abs x,y and rel x, y”)

Framed Panels and Nested PCB’s using KiCad and OSHPark

This continues my earlier post.

About OSHPark

Here is an Element14 article about the OSHPark business model and company history.

Briefly, it says that OSHPark adds value for small makers by aggregating small boards from many customers into large boards that PCB manufacturers are equipped to make.  Some of added value is the software that runs the OSHPark business:  the web store, the software that aggregates many small PCB designs into a larger one, the software that understands and converts many different design file formats.

I appreciate their work and wish them continued success.

OSHPark and ad hoc panels (without frames)

A panel created according to my earlier post does not have any explicit support tabs or a frame.  I will call this an ‘ad hoc panel.’

OSHPark adds support tabs for you, between the PCB’s of your ad hoc panel.  (They also add support tabs on the outside of your panel, to the adjacent customer’s board or panel in the manufactured, huge, aggregate board.)

Unfortunately, OSHPark might also break apart your ad hoc panel at their convenience or need.  OSHPark breaks apart their aggregate, huge panels into customer’s boards.  While they are doing this, they don’t know which groups of boards are a customer’s ad hoc panel.  So OSHPark may break apart your ad hoc panel.

Just looking at the aggregate panel pictured in the above linked article, you can see that it might be difficult for the person separating customer’s boards NOT to break apart your ad hoc panel.

My point is: if you want your panel to stay together, you need to add a frame.  Otherwise, you are taking your chances.  The frame is what tells OSHPark not to break apart the boards.

Why create panels?

  • so you can save labor by stenciling or assembling boards in panels/batches
  • to save board costs by nesting odd-shaped boards

For the first purpose, the fact that OSHPark may break apart your ad hoc panel might defeat the purpose.  In my experience, making panels of 3 small boards, to stencil them by threes, for hand pick and place, it has not been a problem.  If OSHPark breaks apart your ad hoc panel in odd ways, you can always break them into individual units and stencil them by ones (using a corner of the same stencil for the whole panel.)

As I mentioned in an earlier post, OSHPark doesn’t always break your multiple board order into individual boards.  Sometimes you get ‘happenstance panels’ even if you did not order an ad hoc panel.  (Assuming your boards are much smaller than OSHPark cardboard shipping envelopes of about 5×7 inches.)

I am not really serious about volume production yet.  If you are, you should frame your panels.

Nesting odd-shaped boards into ad hoc panels

Say you have an L-shaped board.  OSHPark calculates the cost from the bounding rectangle.  That is, they will charge you for the empty space between the arms of the L.

If you nest two such L-shaped boards into an ad-hoc panel, the cost for the bounding rectangle of the panel will be less than twice the cost of the individual boards.

Other words for nesting are tiling or bin packing.

Tiling is partly how OSHPark subsidizes small makers, by fitting small boards into the wasted space of larger, odd-shaped boards.  If you create your own tiled, ad hoc panels, you save costs, but then someone else might be less subsidized.

 

 

Understanding clocks on Nordic nrf5x embedded radio, towards a long clock

About

This is a quick summary of other posts.  I disambiguate terms in an object oriented way.   I explain elementary things.

I capitalize words to emphasize they are well-known objects, with well-known data and behaviour. You should verify, I could be wrong and I wrote this in haste.  Errors here might illustrate the confusion surrounding the subject.

See Also

A post on Nordic DevZone.

A blog about the nrf5x Application Timer library.

Disambiguation

Time (I don’t discuss, I assume an intuitive notion suffices.)  Subclasses are:

  • UnsynchronizedShortTime
  • RealTime

UnsynchronizedShortTime is a basic Counter.  It is not expected to be synchronized with other instances.  It rolls over every few days.

RealTime is the notion of time used in the real world.   It takes many bits to keep RealTime that is precise to seconds and won’t roll over within centuries.   It takes even more bits (say 64 bits) to keep RealTime that is precise to micro seconds (uSec.)   RealTime is expected to be synchronized with other RealTimes.

A Clock keeps time.  Methods are:

  • set the time
  • ask for the time (now, or current time)
  • set alarms to go off at a certain time.

Subclasses of Clock:

  • RealTimeClock
  • LongClock

A RealTimeClock  is a hardware Device or Peripheral.  It comprises a Counter and a CompareRegister.  The Counter is free-running.  You can configure the RTC so that when the Counter equals the CompareRegister, an Event and/or Interrupt is generated.  A RTC Counter is typically 24-bits.  There is some ambiguity here:  a RealTimeClock does not always keep RealTime and need not be synchronized or accurate.

There is much ambiguity here:  some use RTC to mean real time counter, some use it to mean real time clock.  Some use Real Time Counter to describe a Clock (having alarm functions.)

A LongClock is a longer running clock (before it rolls over.)  A LongClock is typically implemented in software using a RealTimeClock. A LongClock may keep RealTime, or not.

This is where you typically find ambiguity: some “RTC IC’s (chips)” implement RealTime in hardware (they keep time very accurately, and have many bits of time available in a format you typically use in the real world e.g.  YYYY:MM:DD:SS. )   RealTimeClock’s embedded as a Peripheral of a SystemOnAChip (SoC) typically do not implement RealTime (although they can be accurate.)

Precision and Accuracy  and Drift (see wiki.)  Accuracy and Drift are the same notion describing the relation between two Clock instances.

RealTimeClock subclasses:

  • RTCByRCOscillator
  • RTCByCrystal.

They differ in accuracy.

A RTCByRCOscillator uses a resistor and capacitor oscillator embedded in the hardware.   Its accuracy is typically worse than 200 ppm.  That gives a drift of a second every few days.

A RTCByCrystal uses a resonant, ceramic crystal (and sometimes capacitors) external to the hardware.   (There is some ambiguity here:  the circuit the crystal is part of can also be called an oscillator.)  Its accuracy is typically better than 20 ppm.  That gives a drift of a second every month.

A Timer just sets off an alarm.  You can’t tell Time from a Timer (unless you record when you started the Timer.)  You can ask a Timer for time remaining, which is a TiA LongClock meDelta (not a Time.)  Timers are implemented in software using a Clock.

Use Cases

A TimeStamp is a read-only copy of a Time instance.  Many applications don’t need TimeStamps, but only need differences between Times. Often you need to compute a Time difference to set an alarm.

You must be careful when computing differences between Time instances if the Clock has rolled over between the two Time instances.  It is useful to keep a LongClock because pragmatically you don’t need to worry about roll over when computing differences.

Generally Times are kept as unsigned ints.  It is safe to difference two Times if the Clock from which they were taken has only rolled over once between, and if the subtracted Time was actually earlier than the second, later, subtrahend time.  (Since difference of unsigned ints is modulo, like a circular clock.)  If the first time was actually later than the second time, it is not safe to use the difference operator to get a delta.  And it is not safe if the Clock has rolled over twice between.

Design considerations

A RTCByRCOscillator embedded in a SoC is free, using no extra components or board space.

A RTCByCrystal requires an extra component and board space.  A crystal is typically $1.

An external RTC IC requires more board space for itself, and also for a crystal.

A LongClock implemented in software using an embedded RealTimeClock:  the software is free (except for the hassle of finding/writing it) but might still require a crystal.

In general, low-frequency (low precision or resolution) clocks take less power.  Because in general, in CMOS semiconductors, it is the switching of flip-flops that takes power.

How LongClock is implemented

Keep upper (most significant) bits in a memory word.  Keep the lower bits in a RealTimeClock.  Configure the RealTimeClock to trigger an event when it rolls over.  The handler for the roll over event increments the upper bits memory word.

How Timers are implemented

Usually a library provides multiple, concurrent Timers.  The library uses one Clock.  Keep an ordered queue of alarm times of the timer instances.  When the clock alarm goes off, reset the clock alarm for the next timer in the queue (set the counter to the DeltaTime from now to the next alarm time.)

Specifics for the nrf5x embedded radio chip

Since Nordic>InfoCenter>NRF52 Series>Product Specification> :

  • CLOCK
  • RTC
  • TIMER

The CLOCK section actually describes Counters.  The nrf5x has:

  • low-frequency Counters that can be ByRCOscillator or ByCrystal (if you add an external crystal.)  The frequency is 32kHz.  Called LFRC and LFXO.
  • high-frequency 16Mhz  Counters.   The crystal is not optional.   Its accuracy is needed by the radio.  These use much more power than the low-frequency counters.  Called HFRC and HRXO.  To use the radio, you must start the HFCLK.  Then the radio automatically switches between HFRC and HRXO as needed (switching it to RC source to save power when not transceiving.)

Not all modules (boards with an nrf5x on them) have a 32-kHz crystal.  E.g. the RedBear BLE Nano does, the Rigado BMD-300 doesn’t.  They MUST have a 16MHz crystal (or else they have low-performance radios, or will they even work at all?)

The RTC section describes an instance of RealTimeClock (having a compare register.)  Its source can be either of the low-frequency Counters.   It is 24-bits.  Its roll-over depends on the prescaler.

The Timer section describes another instance of RealTimeClock.  Its source is the HFCLK, whose source is either of the high-frequency Counters.

The app_timer library provided by Nordic uses the RTC.  It provides Timers (alarms in the future.)  It allows many concurrent Timers.

Implementing a LongClock using Nordic app_timer.  This is really my goal, and where I leave off until I figure it out.  I want a LongClock (with usec resolution) so I don’t need to worry about roll over, but I want to use app_timer API so I don’t have to worry about low-level interface to the RTC.

 

Setting up Linux/Eclipse for Nordic embedded wireless development

About

These are rough notes about my experience.

The Nordic tutorial.  This post elaborates, because the tutorial is somewhat incomplete, and assumes you are using Eclipse on a Windows host.

Broad overview of the development environment

Nordic seems to support this development environment (Linux, Eclipse, GNU, SEGGER).

They provide example apps that include Makefiles for this environment.

The Nordic tutorial includes Eclipse projects.  The projects use soft file links (“linked resources” in Eclipse terminology.)

The toolchain is not very modern with respect to finding and declaring dependencies:  you must maintain your own Makefile (starting from one provided by Nordic in their examples) and you must create new soft linked resources in the project.  (When you change your project to call additional functions in the SDK.)

Components:

  • IDE: Eclipse
  • compiler: GNU gcc compiler for ARM
  • debugger: GNU gdb for ARM
  • interface to the dev board:  SEGGER tools, JLink
  • flashing: nrfjprog command line tool

My host platform is a clean Ubuntu 16.04.1 installation.

My target is the nrf52 family (nrf52832 chip) on a development board from Nordic, which includes a SEGGER brand debugger probe (JTAG interface.)

Overview

These are the rough steps:

  1. Preliminaries: install your usual but optional tools like git and Dropbox
  2. install a JRE (Java runtime needed by Eclipse)
  3. install Eclipse installer
  4. install Eclipse C/C++ (an IDE)
  5. install ARM plugin for Eclipse
  6. install GNU ARM tools (gcc-arm-none-eabi and arm-none-eabi-gdb, needed by ARM plugin for Eclipse)
  7. install the Nordic nRF5x SDK (libraries used by your app)
  8. install SEGGER JLink tools (interface to the debugger probe on the target development board)
  9. install nRF5x command line tools (to flash the target)
  10. import an example project into Eclipse
  11. choose a Dev pack for the project (a dev pack is a description of the target, optional for debugging)
  12. modify the project and its Makefile
  13. build
  14. flash
  15. debug your example project

Details

These are selected details about the steps.  (But I gloss over other details that are easily searched.)

  1. Since I keep my projects in local git repositories and use the “Team” functions of Eclipse:
    sudo apt-get install git
  2. Eclipse is written in Java:
    sudo apt-get install default-jre
  3. At the Eclipse website you first download a small installer app.  (“Ubuntu Software” app will let you install an older Eclipse.)
  4. Then you run that app and choose from a variety of environments, here C/C++
  5. A third party plugin adds function to Eclipse for developing embedded ARM.  The Nordic chip has an ARM mcu.  Search for “Eclipse ARM plugin” and find the page where you can just drag an icon into Eclipse.  Alternatively, you can use the more usual method of installing Eclipse plugins, that is, choose Help>Install New Software, then enter a URL to the place where this Eclipse plugin lives, etc.
  6. There is a gcc compiler and gdb debugger for ARM that are not installed with the usual development tools (such as make) that are already installed on many Linux hosts.
    sudo apt-get install gcc-arm-none-eabi
    sudo apt-get install gdb-arm-none-eabi
  7.  The Nordic SDK includes many libraries (in source form) and example projects (including Makefiles for this dev environment.)  This toolchain uses soft links to various files in the SDK (the source is not copied to the project.)
  8. The development board includes a SEGGER brand of hardware debugger probe (a JTAG interface to the target) so you are entitled to use SEGGER’s tools to talk to it from Eclipse.   See below.
  9. The nRF5x command line tools are a separate download from the Nordic site.  It yields nrfjprog, a tool to flash your app to the target.  I am not sure you need it, you can also use JFlash, a SEGGER tool?
  10. Outdated example projects are attached to the tutorial as zip files.   The example projects contains an “Eclipse project file”, i.e. it is not just the source files, but a file that configures Eclipse with many soft links to the SDK.    Example Makefiles and .ld files are in the SDK.  E.G. $(NRF_SDK_ROOT)/examples/peripheral/blinky/pca10040/blank/armgcc/Makefile, but using those you must create your own Eclipse project.
  11. ARM Eclipse plugin has support for Packages Manager.   It is optional, but especially needed if you want to use a debugger and examine registers and other memory locations.  See below.
  12. See below
  13. At this point, an example project might build successfully.
  14. See below
  15. See below

More details of installing SEGGER tools (step 8, 15)

The ARM Eclipse plugin has support for the SEGGER debugger probe, but you need to download the SEGGER tools separately from the SEGGER website.  It comes as a .deb package.  Clicking on it opens the “Ubuntu Software” app.  Choose the “Install” button.  It installs executables to /usr/bin.

To test SEGGER tools, plugin your dev board, and at a terminal:

JLinkGDBServer

Expect many lines to scroll by, such as:

Connecting to J-Link...
J-Link is connected.
Firmware: J-Link OB-SAM3U128-V2-NordicSemi compiled Jul  5 2016 08:42:09

This means the host communicated with the probe side of the dev board.  But it will quit because it cannot connect to the app process running on the target side of the dev board?

The Nordic tutorial describes configuring the GNU ARM Eclipse plugin for debugging.

The GNU ARM Plugin site gives a better tutorial for configuring a “Run/Debug Configuration” for SEGGER JLink. Note that for non-embedded C programming, a debug configuration usually exists already, but here you must create one.

After you create a debug configuration, make sure you choose the correct one:  the arrow to the right of the Debug icon lists the configurations.  You will have two after you create another one.  If you choose the wrong one (the C/C++ Application configuration)  you get:

nrf52832_xxaa.out: cannot execute binary file: Exec format error

Note that the “Main>C/C++ Application” field is  “_build/nrf52832.out”,  not .elf.  Other than that, I only entered the device name field, “nRF53832_xxAA”.  (Since I had not already loaded a dev pack it was not filled in automatically?)

Do not choose “Connect to running target” because you usually want to reset the mcu before you start debugging.

When you debug (for example by clicking on the Debug icon), expect Eclipse to change to the debug perspective and for the debugger to break at main().  This worked for me even though I had not flashed the test program, i.e. it stopped at the standard location for main.

More details of installing packs

Follow ARM Eclipse plugin has support for Packages Manager.

In Eclipse, switch to the Packs perspective.  Click the refresh button at the top of the “Packs” pane.  It takes many minutes to refresh (from the web.)  If it fails to refresh packs, try deleting the Packages folder.  Expect a list to appear in the Packs pane.

The left pane is for filtering the Packvoid RTC1_IRQHandler(void);s pane.  If you select an entry there, it filters the list in the Packs pane.

I selected the “Device” tab in the left, filtering pane, and selected “Nordic Semiconductor>nRF52 Series.”  Then in the “Packs” pane I expanded the “nRF_DeviceFamilyPack” item, and right-mouse-button clicked on the latest version and chose “Install” from the pop-up menu.

I don’t understand what the “Board” tab does, and I didn’t use it.

After you install a device pack, the debugger magically (without you needing to change the debug configuration) starts showing more information, such as dissassembly and registers.  Until you do this, they are empty or blank.

More details of futzing with the project (step 12)

Fix path variables in the project

In Project>Properties>Resource>Linked Resources>Path Variables, add a new entry “NRF_SDK_ROOT” to point to where you installed the SDK.

In …Linked Resource>Linked Resources, edit each entry that is broken (has an exclamation point) to use the NRF_SDK_ROOT path variable you just defined.

Fix the path to the compiler in the Makefile

(This is described in the Nordic tutorial, but obliquely, for Windows.)

The Makefile in the example project includes a Makefile.posix from the SDK.  Makefile.posix defines the path to the gcc compiler for ARM.  Edit $(NRF_SDK_ROOT)/examples/toolchain/gcc/Makefile.posix.  Change the first definition (of GNU_INSTALL_ROOT) to have the value “/usr”

Fix capitalization errors (case) in the SDK

The SDK was developed on a Windows host, which is case-insensitive to filenames.  There are some capitalization changes (bugs that Nordic is aware of) needed:

$(NRF_SDK_ROOT)/components/toolchain/gcc/gcc_startup_nrf52.S => .s

In the Makefile:

CMSinclude/Include => cmsis/include

Define path variables in the Makefile

Do something like

NRF_SDK_ROOT = /home/bootch/nrf5_sdk
TEMPLATE_PATH = /home/bootch/nrf5_sdk/components/toolchain/gcc

More details on a Flash target (step 14)

The Makefile has a target (something you can ask to make) for flashing.  The target invokes the Nordic command line tool nrfjprog.  (In more modern toolchains, for non-embedded programming, you neither maintain the Makefile nor invoke more than one make target.  For desktop app, other targets might package an app.  For embedded, you must flash the app.)

You can configure Eclipse so that you can click an icon to make that target (and flash your app to the dev board):  in the C/C++ perspective, in the right pane, select the “Make Target” tab, next to the “Outline” tab.  Then select your project in the body of the pane.  Then choose the “New Make Target” icon (an icon of green, concentric rings with a plus sign.)  Expect a dialog to open.

Enter a “Target name” (extracted from the Makefile), in this case “flash”.

Choose OK.  Expect the dialog to close and a new entry named “flash” in the body of the pane.

To flash your app, double-click that new entry .  Expect the console to show messages about the progress of the flash.

The tool nrfjprog must be in your shell path, or you can modify the makefile to invoke it using an absolute path to its installed location.

More about debugging (step 15)

When you click on the Debug icon (and choose the proper debug configuration) the perspective changes to the Debug perspective and the program is halted at main() (a default of the configuration?)  You can choose from the icons to Resume, Step Into, etc.

If you have installed a dev pack, the Dissassembly tab of the right, middle pane will show assembly code (sometimes?)  The Outline tab of the same pane will show the context of the current program counter?  Sometimes it shows that you have wandered into an exception handler (a green dot?)

The left, middle pane has an arrow in the left margin showing where the program counter is in the source code.  If the PC is in a macro such as APP_TIMER_INIT, that is not very useful.

If you are stepping along and the console shows “Starting target CPU…” and the blue arrow does not advance in the source code, it means you have thrown an exception and entered an infinite loop in the default exception handlers (see startup_nrf52.s).  Click the Pause icon to see where you are looping.  Then the Debug pane in the upper left will show you the call stack and you can examine it to see where in the code you were when the exception was thrown.

Debugging using NRF_LOG

This section is work in progress, not working yet because NRF_LOG has C linkage, not C++

This section is highly dependent on SDK version, here using v12.

The two usual techniques for debugging are:

  • use a debugger e.g. gdb
  • use “printf” or “log” statements sprinkled through your code

NRF_LOG is a logging library from Nordic.  A logging library is glorified printf:  it adds the capability to specify a logging level such as: INFO, DEBUG, etc.  The level can change for each module.

Configuring your project for logging

NRF_LOG requires use of a UART peripheral.   In your sdk_config.h (if SDK v12) file:

#define USE_UART 1

Inserting logging code into your source

At the top of each file (module) where you want to call logging functions to write to the log:

#define NRF_LOG_ENABLED 1#include "nrf_log.h"

Note the order is important, define before include?

At the top of main.c, where you initialize logging:

#define NRF_LOG_ENABLED 1
#include "nrf_log_ctrl.h"

That might require to add more linked resources to your project (depending on whether you are already using a UART) as evidenced by compiler or linker warnings.

Early in main.c, insert and call from main():

static void initLogging(void)
{
    // Initialize logging library.
    __attribute__((unused)) uint32_t err_code = NRF_LOG_INIT(NULL);
    // APP_ERROR_CHECK(err_code);
}

Here NULL means: without timestamping.  I chose to ignore any error return.

Logging to the Eclipse console

TODO Does this work????  The log can go to the Eclipse console. The log statements will be interspersed with messages from the SEGGER GDB Server (e.g. “Reading registers…”)

To arrange that in Eclipse, click on the arrow next to the Debug icon.  Expect a pop-up menu.  Choose “Debug Configurations.”  Expect a dialog.  Select your configuration in the left pane.  Expect the right pane to refresh with the attributes of your configuration.  In the right pane, choose the “Startup” tab.  Choose the “Enable semihosting” checkbox and the “GDB client” checkbox.  Choose “Apply” and “Close.”

Logging to SEGGER RTT

You can also use a faster solution, SEGGER RTT. Faster means it takes less cpu cycles away from say your ISR routines.  It is faster because the logging goes directly from your target mcu to a terminal app on your host, without going through the GDB Server on your host?

Described by E. Styger’s post.  That requires more setup, and requires a SEGGER probe (which your nrf52 DK board has.)

Also described by this post.

Also a Nordic tutorial.

Debugging off in the weeds, lost in never never land

Especially if you are debugging your own code (versus an example project), you might end up with the program state “Running” but not hitting any of your breakpoints you expect to hit.  When you click the “Pause” icon, it might show the PC in:

  • WDT_IRQHandler
  • HardFault_Handler

I think this means an interrupt occurred for which you did not define a handler.  Default handlers are provided in startup_nrf52.s.  You can also see them in the  .map file.

Each of the internal interrupts (for exceptions such as invalid op code, invalid memory address, etc.) has a separate default handler defined.  They are all just infinite loops, but at different addresses.

The default handlers for IRQ (external interrupts) are all just one, same handler (at the same address) that is an infinite loop (assembly code:  “BR  . ”  that is, branch to the value of the current PC program counter register, i.e. a loop.)

WDT_IRQHandler

The debugger will say it is the WDT_IRQHandler, because that is the last name that points to this location.  But it might not be the WDT device that interrupted, and started looping in the default handler.  Once you are paused, to know what IRQ device interrupt occurred,  examine the XPSR register.  One of its sub-registers is the IPSR register, the low-order 9 bits.  (For unknown reasons, SEGGER and gdb did not show the IPSR sub-register by itself, and showed the contents of the full  XPSR register in decimal.)  Convert to hex (using a calculator) and mask off all but the low-order 9 bits.  From that value, subtract decimal 16 (hex 10).  That gives you the index of the interrupt.  Then examine /components/device/nrf52.h for its list of the interrupt indices.  In my case, I got zero, meaning the “POWER_CLOCK” IRQ.

Then you can define your own handler in C, just a function with the same name e.g. WDT_IRQHandler.  The default handlers are defined as “weak” in assembly language, so defining the same name function in C overrides the default.

Here, I am using C++ compiler so I am careful to specify C linkage so that the compiler doesn’t mangle the name and the linker then overrides the default, weak definition:

extern "C" {
void WDT_IRQHandler() {
    while (1);
}}

In my case, the interrupt I was not handling was one used by app_timer.  In the SDK /components/drivers_nrf/clock/nrf_drv_clock.h  I added this (so that the C++ compiler did not mangle the name and thus fail to override the weak default):

#ifdef __cplusplus 
extern "C" { 
#endif
void POWER_CLOCK_IRQHandler(void);
#ifdef __cplusplus 
} 
#endif

Also in components/libraries/timer/app_timer.c

#ifdef __cplusplus 
extern "C" { 
#endif
void SWI0_IRQHandler(void);
void RTC1_IRQHandler(void);
#ifdef __cplusplus 
} 
#endif

This is only because I used app_timer from the Nordic library.  In general, if you use more from the library that might use interrupts, you should examine your .map file for any “…IRQHandler” whose name is mangled by the C++ compiler.

If you fail to make these changes (and you are using C++ and app_timer) the symptoms will be: your app compiles but halts, showing a stack trace that ends in some IRQHandler, after a call to an app_timer function.

Note that Nordic does NOT fully support C++.  Thus you may find instances such as above, where you need to alter the SDK code to support C++.  There are many places in the SDK code that seem to support C++ (i.e.  #ifdef __cplusplus) , but the support seems to be incomplete.

(Here I rant:  I hate C++ but I like object oriented programming.  I use C++ in a highly constrained way.   I don’t want to use a C standard dated to 1999. I need all the help I can get from a compiler.  I wish Nordic would embrace C++, or even some other modern language like Python or Rust.  )

HardFault_Handler

See here.

A short summary is: many bugs (programming errors) such as bad pointers end up as hard faults.  (And not necessarily an op code exception or a memory exception, depending on how exceptions are enabled?)  The debugger often does not show you the call stack when a hard fault occurs.   So install a handler for this exception that helps the debugger show you the call stack when the exception occurred.  A few statements/instructions before the last call is often the errant code.

After thoughts

The build system takes much hands on maintenance of the Makefile, and is not as smart as some build systems about avoiding redoing steps that don’t need doing:

  • build, it only compiles.
  • flash, it compiles then flashes
  • debug, it builds, then starts the debugger, without flashing!

Some of the dependencies are defined in the Makefiles which you are maintaining.  If you create a dependency error in the Makefile, it might build with stale code.

Other build steps are specified in the Eclipse configuration.

If the debugger ever seems lost, start over by cleaning, building, flashing, and debugging.

 

 

 

 

Patching (for Linux) lib_search tool in TI’s tool chain for embedded wireless development

Context

General

Trying to use CCS Desktop on Linux or OSX to build SimpleLink example programs for TI’s embedded wireless chips such as the CC26xx family of chips.

Specific

Fixing a Windows specific tool invoked by the projects of the examples.

References

A one-year old post on TI’s forum

TI’s wiki page for Linux

TI’s wiki page for OSX

TI’s wiki on Portable Projects

Why do you need the lib_search tool?

As far as I know, certain needed code is shipped as pre-built libraries ( .a files.) The example projects are not configured to build the libraries, only to link them in.

The lib_search tool does some sort of configuration.

Aside

I don’t understand what exactly the tool does, why all the libraries are not just in some directory that the linker searches for needed unresolved symbols.

The references seem to suggest that the libraries CAN be built on the Windows platform.  That seems to imply the library source code is not proprietary, i.e. not secret, only copyrighted and licensed.  It seems like it SHOULD be possible to build the libraries on any host system.

How so ever the libraries get built, they are hardcoded for a specific target chip, and possibly for other code such as a version of TI-RTOS?  Or does it pertain more to what the app image needs from the stack image of the embedded program?

Anyway, you don’t need to understand it to patch it.

Where in the project build is the tool invoked?

The example projects are configured to use the tool as a ‘pre-build’ step.  To see that, highlight a project in the Project Explorer pane of the CCS Desktop IDE, click on it with right mouse button, and choose ‘Properties.’  Expect a dialog.  Select ‘Build’ in the left pane of the dialog.  Expect the right pane to show many tabs. Look under the ‘Steps’ tab for a ‘Pre-build steps’ section.

Symptoms of non-portable tool use

When you build on a non-Windows platform you might get:

error #10234-D: unresolved symbols remain

that is, the linker could not find said certain libraries, because the linker command file was not built by the pre-build step.  The root cause is visible much earlier in the log of the build, the pre-build step (lib_search) failed and the build process ignores the failure (proceeds to compile and link.)

The non-portable aspects of the tool

The pre-build step invokes lib_search.exe.  That is a python program (and the interpreter) that has been packaged/bundled for Windows OS (so that python does not need to be installed onto the host.)

(Strange: the BT SDK developers don’t support non-Windows platforms, but they use the tool Python which Windows OS does not support, and they used the tool py2exe so they can ship the SDK portable to any stock/virgin Windows host.)

But as of BT SDK v2, the python source is shipped with the SDK.  So on any host where python2.7 is installed (most Linux/Unix and OSX hosts), and where certain other dependencies (the python package ‘lxml’) are installed, the tool will run as interpreted python.

Old patch for the problem

Some of the references suggest installing Wine and other tools that let Windows executables run on Linux/Unix hosts.

Then the fix is (roughly speaking) prepending wine to the command:

lib_search.exe ...     =>     wine lib_search.exe....

Another patch for the problem

Here we invoke the python interpreter on the python source.

Then the patch is (roughly speaking) prepending ‘python’ to the command and changing .exe to .py:

lib_search.exe ...     =>     python lib_search.py  ....

Here, we assume  “python” invokes python2.7 (which it often does, except if you have changed it to invoke Python 3 or later.)

For this step to work, you must install the python packages that lib_search uses, and the package’s dependencies (lib_search depends on python package lxml which depends on C libraries libxml2 and libxslt.

To install those dependencies:

pip --upgrade pip
pip install wheel
pip install lxml

More exactly, in the build step change:

"${TOOLS_BLE}/lib_search/lib_search.exe"

to

python "${TOOLS_BLE}/lib_search/src/lib_search.py"

More to the patch

Define and redefine certain project path variables:

In the project’s Properties>Resources>Linked Resources>Path Variables  redefine

TOOLS_BLE  <= ${BLE_SDK_ROOT}/tools

and define

BLE_SDK_ROOT <= /home/<you>/ti/simplelink/ble_sdk_2_02_00_31

Change the .xml configuration file

(e.g. in TOOLS_BLE/lib_search/params_split_cc2640.xml)

Use a text editor to change “\” to “/” everywhere in file paths.  (Windows uses backslash in paths, other OS’s use forward slash.)

If you haven’t done this step, you see an error message that lib_search emits, saying

Cannot match   <foo> to any library .opt file in <bar>

where bar is a file path like “…./.\host….”