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 calledwinepath
which is compatible and offers compatible syntax. To use that,winepath
(which itself is a shell script) has to be copied ascygpath
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 settingCONVPATH
tocygpath
orwinepath
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:
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
:
Select Serial Driver Support
:
Now we see that UART1
is going to be used:
Let’s check the baudrate by checking it’s 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: ¯\\_(ツ)_/¯