Cocoacrumbs

Cocoacrumbs

All of the buildings, all of those cars
were once just a dream
in somebody's head
Mercy Street - Peter Gabriel

Cocoacrumbs

12 minutes read

Pic 1

Let’s do a few more episodes on the eZ80, “I don’t like Windows.” and “Why do you want to do/know that?”. This time I will try to get NuttX run on an eZ80.

What is NuttX?

Taken from the NuttX home page:

NuttX is a real-time operating system (RTOS) with an emphasis on standards compliance and small footprint. Scalable from 8-bit to 64-bit microcontroller environments, the primary governing standards in NuttX are Posix and ANSI standards. Additional standard APIs from Unix and other common RTOS’s (such as VxWorks) are adopted for functionality not available under these standards, or for functionality that is not appropriate for deeply-embedded environments (such as fork()).

As far as I can see, NuttX got ported to the eZ80 cpu since version 6 (NuttX reached version 12.1.0 at the time of writing). A board folder for the Z20X computer I have, seems to be available since version 9.

Since it seems appropriate to use a version of NuttX that already has a configuration available for a computer I own, I started my attempt to compile NuttX version 9 (there are reasons why I didn’t choose to simply use the latest and greatest version) on my Linux PC with the Zilog ZDS II tools running under Wine. See a previous blog post on how to use the Zilog ZDS II tools under Linux.

What followed was 2 weeks of long evenings and occasionally wanting to throw the towel in the ring attempting to compile NuttX for the eZ80… That I had no previous experience with NuttX and its build system (and not that much real world experience with the eZ80 neither) didn’t help me of course.

Compiling NuttX

Going through the various README files, I bumped into this paragraph:

I’ve never tried this one, but I off the following reported by an ez80 user using the ZiLOG ZDS-II Windows-native toolchain:

“I’ve installed ZDS-II 5.1.1 (IDE for eZ80-based boards) on wine (windows emulator for UNIX) and to my surprise, not many changes were needed to make GIT snapshot of NuttX buildable… I’ve tried nsh profile and build process completed successfully. One remark is necessary: NuttX makefiles for ez80 are referencing cygpath utility. Wine provides similar thing called winepath which is compatible and offers compatible syntax. To use that, winepath (which itself is a shell script) has to be copied as cygpath somewhere in $PATH, and edited as in following patch:

“Better solution would be replacing all cygpath references in Makefiles with $(CONVPATH) (or ${CONVPATH} in shell scripts) and setting CONVPATH to cygpath or winepath regarding to currently used environment.

This gave me some confidence that what I was trying to do should be feasible. In the end it turned out to be a bit more complicated than thought. But isn’t this always the case?

Obtaining NuttX

On this web page you can download tar balls for a specific release. I opted for the latest release of version 9, which is 9.1.1. Click on both the OS and apps links to download the source code tar balls for both NuttX and NuttX applications.

After unzipping the tar balls, you should have both a nuttx and apps directory. For the rest of this blog post, you have to assume that you’re standing in the nuttx directory for executing commands on the command line.

I’ll also assume you have all required development tools installed in your Linux setup. Including Kconfig-frontends (needed to configure NuttX).

Configuring NuttX

Before you can start compiling the code, you need to configure NuttX for a certain board/architecture. Quite a few configurations are already available and a list can be obtained with this command line:

./tools/configure.sh -L | less

A long list of available configurations will then be dumped to the terminal. The eZ80 related configurations are:

makerlisp:nsh_ram
makerlisp:nsh_flash
makerlisp:sdboot

ez80f910200zco:dhcpd
ez80f910200zco:httpd
ez80f910200zco:poll
ez80f910200zco:nsh
ez80f910200zco:nettest

ez80f910200kitg:ostest

z20x:sdboot
z20x:w25boot
z20x:nsh
z20x:hello

The simplest configuration (for me, since I have a Z20X computer) was z20x:hello. So I concentrated first on this configuration and try to get it to work.

Normally, we now issue this command:

./tools/configure.sh z20x:hello

And we immediately run into problems:

    $ ./tools/configure.sh z20x:hello
    Copy files
    Select CONFIG_HOST_LINUX=y
    Refreshing...
    /bin/sh: line 1: cygpath: command not found
    /bin/sh: line 1: cygpath: command not found
    ...

The ./tools/configure.sh tool does accept a few command line options:

USAGE: ${0} [-E] [-e] [-l|m|c|g|n|B] [L] [-a <app-dir>] <board-name>:<config-name> [make-opts]

Where:
  -E enforces distclean if already configured.
  -e performs distclean if configuration changed.
  -l selects the Linux (l) host environment.
  -m selects the macOS (m) host environment.
  -c selects the Windows host and Cygwin (c) environment.
  -g selects the Windows host and MinGW/MSYS environment.
  -n selects the Windows host and Windows native (n) environment.
  -B selects the *BSD (B) host environment.
  Default: Use host setup in the defconfig file
  Default Windows: Cygwin
  -L  Lists all available configurations.
  -a <app-dir> is the path to the apps/ directory, relative to the nuttx
     directory
  <board-name> is the name of the board in the boards directory
  configs/<config-name> is the name of the board configuration sub-directory
  make-opts directly pass to make

On my Linux setup, the configure.sh script already selected Linux as the host. And I decided to add a new command line option -w to indicate that I want to use the Wine environment on Linux. The command line to use becomes:

./tools/configure.sh z20x:hello -w

Next to that, I introduced a new environment variable filled in with either cygpath or winepath depending if the host is running Windows or Linux:

# Windows environment identification

ifeq ($(CONFIG_HOST_WINDOWS),y)
    WIN_ENVIRONMENT = "cygpath"
else
    WIN_ENVIRONMENT = "winepath"
endif

In various make files, I then replaced the occurrences of cygpath with $(WIN_ENVIRONMENT).

At the same time, I took the opportunity to introduce more recent versions of the ZDS II toolchains in Toolchain.defs:

else ifeq ($(CONFIG_EZ80_ZDSII_V534),y)
INSTALLROOT = C:/ZiLOG
ZDSVERSION := 5.3.4
else ifeq ($(CONFIG_EZ80_ZDSII_V535),y)
INSTALLROOT = C:/ZiLOG
ZDSVERSION := 5.3.5

And corresponding changes is the related Kconfig file:

choice
    prompt "ZDS-II Toolchain version"
    default EZ80_ZDSII_V535

...

config EZ80_ZDSII_V534
    bool "ZDS-II 5.3.4"

config EZ80_ZDSII_V535
    bool "ZDS-II 5.3.5"

By default, the latest (5.3.5) ZDS II toolchain will now be used.

The next problem was the zdsar.exe utility. This did not compile correctly for me and I had to introduce a define HOST_WINE to indicate that this utility would run on Wine under Linux.

    #elif defined(HOST_WINE)
    #  define SEPARATOR '/'
    #  define HOSTNAME  "Wine"   /* WINE under Linux */
    #endif

Some small changes were made to the nuttx/tools/zds/Makefile make file as well:

    ifeq ($(WIN_ENVIRONMENT),cygpath)
    HOSTCFLAGS += -DHAVE_STRTOK_C=1 -DHOST_CYGWIN=1
    else
    HOSTCFLAGS += -DHAVE_STRTOK_C=1 -DHOST_WINE=1
    endif

After all those, small, modifications in various files, I finally got through the ./tools/configure.sh z20x:hello command without any errors (or warnings).

Running the configure.sh script takes quite some time. Time in which you can easily make a pot of coffee/tee.

Modifying a configuration

If you know your configuration is 100% correct, you can skip this step. But in my case further modifications were required. And it gives us an opportunity to explore the many configurations of NuttX as well. So we type in:

make nconfig

And after a bit of waiting (drinking some coffee/thee) we’re greeted with the following screen:

Entry point to configure NuttX.

Use the cursor keys to explore all the possible options.

For this simple example, I was interested to see which UART was going to be used for the console and at which baudrate. After a bit of exploring, I found that I needed to follow this path:

Select Device Drivers:

Device Drivers.

Select Serial Driver Support:

Serial Driver Support.

Now we see that UART1 is going to be used:

UART1 is used.

Let’s check the baudrate by checking it’s configuration:

UART1 configuration.

Here we see that 57.600 bps is used and 8 bit characters (which seems to be the recommend values for the eZ80 by Zilog). Originally this was set to 2.400 bps and 0 bit characters.

You can exit the configuration tool using F9. If you made any changes to the configuration it will ask if you want to save the changes or not.

Compiling NuttX

Before we can start compiling the source code, I first needed to modify the nuttx/tools/zds/Config.mk file so that the ZDS II tools are called via wine. I did this in the following way:

ifeq ($(WIN_ENVIRONMENT),"winepath")

define CONDMOVE
	$(Q) if [ ! -e $(1) -a -e $(2) ] ; then mv -f $(2) $(3) ; fi
endef

define MVOBJS
	$(call CONDMOVE, $(1),$(subst .obj,.src,$(2)),$(subst .obj,.src,$(3)))
	$(call CONDMOVE, $(1),$(subst .obj,.lst,$(2)),$(subst .obj,.lst,$(3)))
	$(call CONDMOVE, $(1),$(2),$(3))
endef

define COMPILE
	$(call RMOBJS, $(2))
	$(Q) wine $(CC) $(CFLAGS) $($(strip $(1))_CFLAGS) `$(WIN_ENVIRONMENT) -w "$(shell pwd)$(addprefix /,$(1))"`
	$(call MVOBJS, $(2), $(subst .c,.obj,$(notdir $(1))), $(2))
endef

define ASSEMBLE
	$(call RMOBJS, $(2))
	$(Q) wine $(AS) $(AFLAGS) $($(strip $(1))_AFLAGS) `$(WIN_ENVIRONMENT) -w "$(shell pwd)$(addprefix /,$(1))"`
	$(call MVOBJS, $(2), $(subst .asm,.obj,$(notdir $(1))), $(2))
endef

PS. I’m aware there exist binfmt so that Linux can execute other executable file formats but I personally prefer the wine method as I used here. It feels cleaner to me and I don’t need messing up Linux for just this one use case I have.

Now we’re ready to compile NuttX and for that we simply type in:

make

And be prepared to prepare another pot of coffee. You can speed up compilation considerably by using the -j option of make to start compilation tasks in parallel. I have a 12c/24t CPU and I typically use:

make -j 20

This helps to keep your pot of coffee/thee warm as well if you put the pot on top of the CPU.

If this is the first time you compile NuttX I would advise to simply stick to just make because that makes it easier to catch compilation errors/problems. And there were many when I attempted this step.

TIP: to make the output of make more informative (e.g. it will show the command line used to invoke the compiler including all its command line options), you can execute this command line first before calling make:

export V=2

Compiling NuttX quickly grinded to halt for me due to compile errors. E.g. in sched.h, this struct refused to compile:

    struct exitinfo_s
    {
        union
        {
    #ifdef CONFIG_SCHED_ATEXIT
            atexitfunc_t at;
    #endif
    #ifdef CONFIG_SCHED_ONEXIT
            onexitfunc_t on;
    #endif
        } func;
    #ifdef CONFIG_SCHED_ONEXIT
        FAR void *arg;
    #endif
    };

As it turned out, the union was simply empty (both CONFIG_SCHED_ATEXIT and CONFIG_SCHED_ONEXIT were not defined) and ZDS II doesn’t seem to like that (or isn’t allowed in the C89 standard). I solved it like this:

    struct exitinfo_s
    {
        union
        {
    #ifdef CONFIG_SCHED_ATEXIT
            atexitfunc_t at;
    #endif
    #ifdef CONFIG_SCHED_ONEXIT
            onexitfunc_t on;
    #endif
            int stub; /* ZDS II doesn't like an empty union. */
        } func;
    #ifdef CONFIG_SCHED_ONEXIT
        FAR void *arg;
    #endif
    };

A second compile error was given for this:

    void _exit(int status)
    {
        up_exit(status);
    }

ZDS II didn’t like it that the status variable was passed to up_exit() because the prototype for up_exit() is defined as follows:

    void up_exit() noreturn_function;

I.e. no parameter. Hence I modified _exit() to:

    void _exit(int status)
    {
        up_exit(/*status*/);
    }

After which code got compiling again. This kind of compilation errors is also the reason why I did not use a more recent version of NuttX. E.g. in version 10.x I already saw more compilation errors than these 2 I found in version 9.1.1 while I only compiled a few files.

This was another moment where I started thinking of throwing the towel in the ring. The existence of such, small, compilation errors made me doubt that there was a recent smoke test of NuttX, configured for the eZ80, to test if the code actually still compiles, let alone ran fine on the eZ80. I’m not blaming the NuttX team for that. The number of boards and architectures and the enormous amount of configuration possibilities of NuttX makes it impossible to test everything. And the eZ80 is probably way down their priority list.

On the other hand, it was clear that there was once a very sizeable effort to create all those eZ80 specific build files that it would be a shame to lose that effort and code base (NuttX feels as an incredibly clean and well engineered RTOS from what I’ve seen so far). So, I soldiered on.

I should also mention that there is another toolchain to compile NuttX for the eZ80. I wrote about that in an earlier blog post Experimenting with the LLVM/Clang toolchain for the Zilog eZ80. This toolchain should have no problems with the above mentioned source code. I decided against using this toolchain for a few reasons:

  • I don’t feel it’s ready for production code. The Zilog tool chain must have compiled gazillions of lines on real production code. I don’t think that is the case for this new toolchain (yet?). Hence an extra source of potential errors to consider when things go wrong. At this stage of figuring things out, I need to have the least amount of variables that could go wrong.
  • I feel the ZDS II C Compiler produces better optimized code.
  • Maybe most importantly, this new toolchain doesn’t produce binaries that contain debug information that the ZDS II IDE can use.

I did another small change to the nuttx/boards/z80/ez80/z20x/include/board.h file. There the system clock frequency is specified:

#define EZ80_SYS_CLK_FREQ           20000000

This is 20 MHz while the Z20X BOM actually specifies 18 MHz. And the crystal I’m using is 18.432 MHz (because this is a nice multiple of most of the usual baud rates). So I changed it to this:

#define EZ80_SYS_CLK_FREQ           18432000

And this is important when you hook up your terminal emulator later for testing, like minicom, picocom, … to have a better chance in reading what is send out on UART1.

Another problem I encountered is that with the ZDS II tools you can use the inc and usrinc command line option only once and you need to specify the paths together, separated by a :. E.g.:

ez80cc.exe     -usrinc:'<dir1>:<dir2>:<dir3>:...`

This needed a few fixes in the build files as well to work properly for me.

To finish, I made a small change to nuttx/arch/z80/src/Makefile.zdsiil so that .lst files are generated as well for the C source code. This is always helpful when trying to debug not working code.

This is in a nutshell an overview of the various changes and fixes I needed to do to get the simplest Hello World!! program to compile. At this stage, 3 files can be found in the nuttx directory (FYI close to 2.000 files are generated while building this simplest NuttX configuration):

nuttx.hex
nuttx.lod
nuttx.map

And we have now something that we can test in either the simulator or on actual hardware.


The code

What I did was simply downloading the source code tarballs for both nuttx and apps of version 9.1.1.

After extracting both tarballs, I initialized a git repo of it. Then I created my own branch ccc_dev_branch and used this branch to check in all my small modifications that lead to a NuttX that can compile, and finally works on the eZ80, under wine on Linux using the official ZDS II tools from Zilog.

You can find the git repo here.

Disclaimer

Although I verified the build on 2 separate PC’s (but using the same Manjaro Linux Distro) I can’t guarantee it will work for everyone. I guess, the best I can say in such circumstances is: It works on my computer: ¯\\_(ツ)_/¯

Recent posts

See more

Categories

About

Cocoacrumbs