Z80 C code development with Eclipse and z88dk

I’m building a Z180 based development board called the YAZ180 for the 40th anniversary of the Z80 processor. As part of that process, I need to have a development environment that supports the Z80 and the Z180 processors. As I haven’t finished building the YAZ180 yet, I’ll be testing the development environment on the RC2014 platform in the interim.

There are a couple of major differences in the workflow required to program the YAZ180 from the RC2014. The RC2014 requires an EEPROM programmer to burn the resulting HEX file into its ROM. Eventually, the YAZ180 will use a PERL program to manipulate a parallel port to programme FLASH memory. However, for the purposes of setting up a development environment they are essentially the same.

To set up the environment, we’ll need to have:

  • A C compiler suite capable of generating HEX or BIN files for burning onto the hardware.
  • Applicable .CRT files to initialise the CPU and RAM, either Z80 or Z180 specific, so that the C environment can be properly launch.
  • Suitable library files for USART, and other interfaces, appropriate for the hardware in use.
  • Configuration to allow the correct tools and libraries to be found from within the Eclipse IDE.

A C Compiler Suite

There are only a few options for C compilers for the Z80 processor. There is a the Zilog development environment, and the SASM Softools. On the open source side there are two options worth mentioning, being the Small Device C Compiler (SDCC) and the Z88DK Small C Compiler.

There are a few reviews on the Internet of the various options, but in summary the best outcome seems to be to use the Z88DK together with the SDCC Compiler, and the “new library”.

The Z88DK team contributed this information to the RC2014 forum, which gives an overview of the options.

There are two C compilers. One C compiler is sccz80 which is derived from small C but z88dk’s version has seen continuous development over the past 30 years so it’s had most of the limitations of small C removed. For example, floating point is supported, ANSI C declarations are supported, 8/16/32-bit integers are supported and so on. It is a little short of C89 compliance with a few notable non-compliances being multi-dimensional arrays and function pointer prototyping.

The other C compiler is a patch of sdcc, another open source compiler that attempts to implement subsets of C89, C99 and C11. sdcc is an optimizing compiler and z88dk’s patch improves on sdcc’s output by supplying some Z80 bugfixes not yet incorporated into sdcc itself and by supplying a very large set of peephole rules to further improve output.

You can choose which C compiler you use by selecting the appropriate switch on the command line. In your makefile you are using sccz80. To use sdcc, “-clib=sdcc_ix” or “-clib=sdcc_iy” would appear in the compile line.

And then there are two C libraries.

The classic C library is the C library that has always shipped with z88dk. It has many crts available for it that allows compiling for a lot of target machines out of the box. The level of library support varies by target with the best supported having sprite libraries, sound, graphics, etc supplementing the standard c library. It is mostly written in machine code and has a small stdio implementation. However, at this time it cannot be used to generate ROMable code as it mixes variables with code in the output binary. It’s also not compatible with sdcc at this time. Both of these issues are being addressed now.

The new C library is a rewrite from scratch with the intention of meeting a subset of C11 compliance. It is 100% machine code, is written to be compatible with any C compiler, and can generate ROMable code with separation of ROM and RAM data. The stdio model is object oriented and allows device drivers to be written using code inheritance from the library. Although it’s not finished (it’s missing disk io and non-blocking io), it is in an advanced state.

The choice of C library is made on the compile line. “-clib=new”, “-clib=sdcc_ix” and “-clib=sdcc_iy” all use the new C library. Anything else uses the classic C library. In order to generate ROMable code, you should really be using the new C library.

The sdcc_ix and sdcc_iy libraries are chosen when sdcc is the compiler and are selected between by either ”-clib=sdcc_ix” or ”-clib=sdcc_iy” on the compile line. The difference between the two is which index register the C library uses. “sdcc_ix” corresponds to the library using ix and “sdcc_iy” corresponds to the library using iy.

It’s always preferable to use the “sdcc_iy” version of the library because this gives sdcc sole use of ix for its frame pointer while the library uses iy. If “sdcc_ix” is selected, sdcc and the library must share ix which means the library must insert extra code to preserve the ix register when it is used. This means the “sdcc_iy” compile will be smaller.

z88dk’s C library is different from other compilers in that it is written in assembly language, so it is more compact and faster than other z80 C compilers.

Installation instructions for z88dk here and I’d recommend using a nightly build rather than the last release. z88dk is an active project and it changes quite quickly. If you run on windows or mac there are binary packages available from the nightly build. For linux or other targets there are instructions for building from source and for patching sdcc to create zsdcc, z88dk’s version of sdcc.

Just to add for the ROM target: the new C lib allows the stored data section to be lz77 compressed so this should save a few bytes in the stored binary in ROM. Another thing you could do is compile a program for RAM and store a compressed copy in ROM that gets decompressed into RAM at startup.

Z88DK & SDCC Installation

I’m installing Z88DK and SDCC onto Ubuntu 16.04 AMD64 and, since the machine has recently been refreshed, many packages that were required for the install were missing.

Download the latest nightly checked Z88DK source package and unzip it:

wget http://nightly.z88dk.org/z88dk-latest.tgz
tar -xzf z88dk-latest.tgz

sudo apt-get install expect texinfo libxml2-dev flex bison gputils libboost-dev

This will create a populated z88dk directory in the current working directory.

To succeed in building the ‘z80svg’ graphics tool you need the ‘libxml2’ library to be previously installed, although its absence will not prevent the rest of the kit from building.

Then, just type:

cd z88dk
chmod 777 build.sh (just in case)

You can run z88dk keeping it in the current location, all you need to do is to set the following environment variables.

Supposing you have bash (most likely it is your system default shell) and you want to keep z88dk in your home directory, you can configure it permanently in this way:

vi ~/.profile

Modify the configuration by adding these lines (with the appropriate paths).

export PATH=${PATH}:${HOME}/z88dk/bin
export ZCCCFG=${HOME}/z88dk/lib/config

A system install is not supported in this release of Z88DK.

Then to install the SDCC compiled specifically for the Z80 and Z180 these are the instructions.

Check out the current development version of sdcc. If you already have the sdcc-code tree available from a previous checkout you can instead perform an update.

svn checkout svn://svn.code.sf.net/p/sdcc/code/trunk sdcc-code
# or if you're doing this to refresh your sdcc installation...
svn update

You will have to apply the svn patch found in sdcc_z88dk_patch.zip and build sdcc from source. Copy “sdcc-z88dk.patch” from inside sdcc_z88dk_patch.zip into the sdcc-code directory.

The supplied configuration options disables all ports other than the Z80 family ports, and turns off compilation of many libraries. This will prevent errors from completing the build process, and results in a smaller binary.

cd sdcc-code
patch -p0 < sdcc-z88dk.patch
cd sdcc
./configure --disable-mcs51-port --disable-gbz80-port --disable-avr-port --disable-ds390-port --disable-ds400-port --disable-hc08-port --disable-pic-port --disable-pic14-port --disable-pic16-port --disable-xa51-port --disable-stm8-port --disable-tlcs90-port --disable-s08-port --disable-ucsim --disable-device-lib --disable-packihx

Copy the patched sdcc executable to {z88dk}/bin and rename it “zsdcc”.
Copy the sdcc preprocessor to {z88dk}/bin and rename it “zsdcpp”.

cd bin
cp sdcc {z88dk}/bin/zsdcc
cp sdcpp {z88dk}/bin/zsdcpp

Undo the patch.

cd ../..
patch -Rp0 < sdcc-z88dk.patch

You can stop here and verify the install was successful below. Keeping the sdcc source tree in an unpatched state can allow you to update the zsdcc binary by repeating the steps above as sdcc itself is updated. Both z88dk and sdcc are active projects that see frequent updates.

To verify that sdcc is usable from z88dk, try compiling sudoku.c for the cp/m target using sdcc:

zcc +cpm -vn -SO3 -clib=sdcc_iy --max-allocs-per-node200000 sudoku.c -o sudoku -create-app

Using the C compiler

Assuming we have a source code called test.c

#include <stdio.h>

We can compile it and produce binary CODE and DATA sections. The CODE and DATA sections need to be concatenated, and then assembled into an Intel HEX file.

zcc +embedded -v -m -SO3 -clib=sdcc_iy -startup=1 --max-allocs-per-node200000 --c-code-in-asm --list test.c -o test
cat test_CODE.bin test_DATA.bin > test.bin
appmake +hex --org=0x0 --binfile test.bin --output test.hex

The binary code can be checked by installing and then using a disassembler z80dasm

sudo apt install z80dasm
z80dasm --address --labels --origin=0x0 test.bin

RC2014 Troubleshooting

So I soldered it all together, and it doesn’t work. Typical. It looked so easy, all of the instructions are straightforward, and the boards are clear and labeled for easy assembly.

I guess this is the story for many projects and some of them never proceed past this point and end up in the junk box. But, sometimes there’s a guide for what to do when there’s trouble.

So this is my guide to how I fixed my RC2014.

1. Power supply

I installed a 7405 linear regulator into the provided slot on the backplane. Because there was no space for a protection diode I added one in series to the power input terminal. First, remove all of the cards from the backplane. We’ll start with power supplies. Using a 12V supply, let’s check that there is 5V and GND available to every backplane slot.


7805 Regulator with 1A linear diode in Vin.

2. Reset

I have used the new backplane Reset function. Let’s test that it is effective in providing 5V pull up normally, and pull down to GND when the reset button is depressed.

3.Clock Function

The CPU requires a Clock, and that is provided by the small PCB containing the crystal and the buffer amplifiers. I didn’t equip the Reset button or resistor on my build, because 2x resistors is not required, and only one switch was included in the materials provided.

Using an oscilloscope to watch the signal, the performance of the Clock and its crystal can be measured. So the crystal is oscillating and produces 7.353MHz with a good strong signal. And it is available across the backplane to all the slots.

[Add picture later. I’ve lost the USB stick.]

4. CPU General

Insert the CPU module and check that it has 5V power, GND, and the Clock and Reset lines are working as expected.

On the Address lines, there should be a signal at 1.232 MHz, representing the cycle of CPU running NOPs. With this signal in place, we can move on to the ROM and RAM modules.

[Add picture later, I’ve lost the USB stick.]

5. ROM & RAM

Now we check that there is power and ground at both of the ROM and RAM modules. If that is the case then it is back to the logic analyser to check what is happening on the system now that the CPU has access to instructions to read.

6. Logic Analyser

With an 8 input logic analyser we can’t look at all of the signals at one time, so let’s choose some relevant ones. The lower few address lines are interesting, because they show where the CPU is reading instructions as it starts up. Also, if the Serial I/O port is attached then the Tx and Rx lines can be monitored on the backplane too.

Reset the system to see what happens immediately after the Reset is released. Note the ASCII text message appearing on the Tx line on the backplane, noting the Geoff Seale copywrite, the invitation to choose cold or warm boot, and the memory size. This means that the RC2014 is living, but somehow the Serial I/O board is not functioning properly. So we need to focus attention there.

7. Serial I/O

The Serial I/O card should be connected to the FTDI Basic or other FTDI FT232R equivalent device. As I had a PL2303 based serial cable, I decided to use that, as it is much cleaner than using an FTDI adapter, and it allows the Serial I/O board to be positioned anywhere on the backplane.


Prologic PL2303 Serial Cable, exits inline with backplane connector.

The Logic Analyser shows that the 63B50P chip is doing its job and producing characters on the Tx line. But we aren’t seeing characters on the FTDI Rx line on the Terminal. That is a problem. Note that there are 2.2kOhm resistors in series with the serial module Rx and Tx lines. That’s a bit more than I’d expect to see. Let’s reduce those resistors down to around 100 Ohm. They could be 0 Ohm, but it is better to be a bit conservative.

Reducing the in series resistance doesn’t allow either the FTDI or the Prologic interface to work either. There’s something else going on here.

With both the Logic Analyser and the FTDI interface attached to the RC2014 the comforting welcome from Greg Searle appears on the Terminal, urging us to cold or warm boot. Yet, when the logic analyser is removed the serial I/O is no longer working.

This looks like some kind of ground loop problem. I don’t know how this can be fixed easily. The RC2014 device only works when there is a ground provided by another source, such as the Logic Analyser.

8. Other issues

The Serial I/O device was delivered one incorrect IC socket (14 pin instead of 16 pin), so I had to solder one chip directly to the board. Not a big issue.

There is no mention of how the ROM system needs to be configured. There are three options available for selection. The right one is with all of the address lines set to 0.

The basic program is described on Greg Searle’s Simple Z80 web page. There is little mention that this this is where to go for additional software support and programming assistance.

Anyway, with the caviet that the device has to be connected with an external earth, the RC2014 is working perfectly.

9. Earth Issues

After asking Spencer for some ideas, and doing some further circuit testing, I found that I had somehow damaged the ground wire near the centre of the backplane. That was allowing the RC2014 to function when GND was provided by both FTDI and Logic Analyser on opposite sides of the backplane, but to fail when only one GND was provided.

I resolved this by soldering an additional wire along the entire GND line. Whilst  I could have just bridged the gap, I preferred to improve the GND stability by adding conductor all along its length. I covered the wire with hot-glue to increase its stability. As a side effect it adds a non-slip characteristic to the backplane, and helps to protect my desk from being scratched.


GND wire hidden under a protective hot glue sheath.

Now that I’ve fixed my dodgy soldering on the GND line, everything is much better, and works perfectly


The hot glue sheath helps to hold the solder pins off my desk too. Integrated non-slip.

Testing MicroPro TL866

Part of the learning experience around the Z80 processor is to get a good understanding of the methods used to program the EEPROM or Flash memory used to drive old style processors. To that end, I have purchased a MicroPro TL866 programmer.


MicroPro 866 Universal Programmer

The TL866 is produced in China by the AutoElectric company. There is a web site with extensive information about the product. Many resellers exist on ebay, Amazon or other locations. I purchased mine from an ebay seller.
There are quite a number of reviews on the TL866 on the net. I skipped through the EEVblog video, read the comments, and then placed an order. There may be other alternatives around but this one is very cheap, and will meet my needs (provided it works) for the medium term.

The only choice is to purchase the version with or without the ICSP capability, for Atmel and other chips. For me that wasn’t a consideration, as I have an Atmel ICE already. So I saved my money, and bought the packaging option with multiple adapters for many EEPROM packages.

First thing I did when it arrived was to plug it into my office Windows 10 machine, and test whether it would indeed work with a 64 bit OS. I had some doubt about this, because some of the ebay sellers have not updated their descriptions to match the latest software version (v6.50 as written), which now supports Windows 10.

At home I have repeated the process with another Windows 10 machine. I’m disappointed that the TL866 doesn’t support Ubuntu or but for the small money it cost me, I can keep a second machine nearby.

Argh. I’m reminded that Windows means driver hell. I have to find a prolific PL2303 driver before i can plug in my RC2014 to test that it is working still… And of course, windows needs to be rebooted or whatever… Ok. Well that was a waste of 30 minutes. The PL2303 driver won’t install correctly. I’ll come back to that later.

Let’s test to see if we can read the EEPROM installed and delivered with the RC2014. But first, we need to download the latest version of the minipro_setup.exe program which provides the driver for the TL866.

RC2014 Cylon

RC2014 Cylon

Having confirmed RC2014 is confirmed working. The EEPROM is an ATMEL AT27C512R. Selecting the chip from the menu is fairly straight forward. You can search for the actual chip, or just browse for the manufacturer, if you’re not sure. There’s an Information button to show you how to insert the chip into the ZIF socket.


TL866 – Windows10 & RC2014 PL2303 Serial – Ubuntu

The next thing is to test whether the TL866 reads the chip. And, it does that successfully. I’ll save the HEX file, and then clear the chip and save it back again as the next test.
Ok so it programmed the entire chip in 20 seconds, and then verified in 1.5 seconds. Much much faster than a bootloader. Success.

As a final test, in this simple process, I selected the Grant Searle version for the 32k RAM build, to see if another HEX file could be programmed successfully. And, this works too.

So now the next step is to build an environment with C compiler and linker, to allow me to create my own HEX files to load into the RC2014. That might take a few weeks…


Yet Another Z80 Project (YAZ180)

I’m thinking about a new project, something a little unusual but still with a rich history of information upon which to base the build. On Tindie, I found the RC2014 project which is a build of a Z80 platform but based on some modern components. That got me thinking. My next project must be a Z80 based project.

Why the Z80? Well, it was at one stage the most used CPU in the world, which leads to the great depth of information and experience available for designs, hardware, and software. Technically, it is advanced enough to avoid the need for a large number of ancillary chips, multiple power supplies, and multi-phase clocking that the 80088080, and other older chips needed, but still it is complex enough that in doing a project I’ll feel like I’m actually building a computer.

This year marks 40 years (yes 40 years, since launch in July 1976) of the Z80, and still as a design and platform it looks like it will continue to be relevant into the future. So rather than building yet-another-ARM project. It looks like I will be marking the 40th anniversary of the Z80 with a new build.


An early Z80, manufactured in June 1976.

What kind of project? The RC2014 project is an interesting starting point. It is quite simple, being a compact and robust implementation of Grant Searle’s 6 Chip Z80 Computer, but provides more resources than most of the 1980’s Z80 projects offer. After looking at some projects others have done, I think that I should aim to build something a little like a “Back to the Future” Z80 project, a DeLorean (which also appeared as a prototype in 1976) with a fusion reactor.

RC2014 Cylon

RC2014 Cylon

As an outcome I’d like the solution to be able to interact with modern interfaces such as I2C, Ethernet, SPI, and USB, with access to a large physical memory space, with great performance, and yet retain the ability to be a single-step-able experimental platform with LED bus indicators. Single stepping is something that you can’t do with an Arduino, and it is a real differentiation.

Whilst it would be attractive to design in some old-school interface devices, like the AM9511A FPU or a Super I/O device, in light of today’s environment the wouldn’t contribute very much to the outcome. So the focus will be squarely on modern I/O.

Also, there is a temptation to build a CP/M system with full disk management. But again there are plenty of CP/M systems around. I’d like to build something more  of an embedded platform, that doesn’t require mass storage to run applications. It would be good if most of the basic interfaces could fit on one board, to avoid the need to build address and data bus extension. I think this will simplify the design significantly.

Design process

This is going to be an iterative process. The first step will be to build the RC2014 project, and test that I can program it.

It will be important to learn a little about Z80 assembler. Later, I may modify the RC2014 project platform.

Then, I’ll lay out a through hole prototype with minimal functionality, to test some performance ideas. If they work on through hole, then they’ll work with SMD. Using through hole also allows me to quickly fix logic or wiring errors that would take a new spin with SMD.

Finally, I’ll build a SMD device that miniaturizes the solution, and makes it more robust.

Processor selection

The Z80 has been built continuously for 40 years, and in that time many manufacturers have produced silicon and several clones have been created. The Z80 range been continuously improved through the Hitachi 64180, to the Zilog Z180, and the Zilog Aclaim eZ80 devices. Each increment has integrated more accessory components, and improved the instruction throughput, as well as increased the clock rate.

Looking at the options available, the original Z80 requires logic to get started. So rather than building serial ports and timers, it looks like the Z180 might be the right place to start. So why not go all the way to the eZ80? Well the eZ80 is not dissimilar to an AVR ATmega device, with all of the system components integrated into the one device. Using an eZ80 CPU wouldn’t be like building a computer at all. It would be much more like building an Arduino, and I’ve been there already.

Out of the Z180 options, I would select the Z8S180 (at 33MHz) because it integrates sufficient material to get started (Timers, Interrupts, MMU, & USARTs), and leaves me the option to add complexity as I get going.

Memory selection

A little research on the processor and available memory provides me with some cornerstones for the design. I will use Flash memory for the  program storage, and static RAM for the system memory. Historically, UV erasable PROM and dynamic RAM would have been used. One advantage of static RAM is that the solution is fully single-step-able. Meaning, I’ll be able to watch the address bus and data bus process each instruction as the Program Counter marches along.

The Z180 can address up to 1MB of physical address space, and it makes sense to provide the full physical memory possible. The price of 1MB of SRAM or of 256kB of Flash is almost nothing these days. As the Z80 logical address space is only 64kB, the Z180 has an inbuilt MMU to manage its physical memory to logical memory mapping.

Memory mapping

To keep things simple (in hardware), we can use the MMU available in the Z180 to map the physical memory locations on 20 address lines, into logical addresses that suit us. The MMU can map 4kB pages of physical memory into two relocatable logical locations in the Z80 logical address space. These are called the Banked and the Common 1 locations. The Common 0 memory location begins at physical address 0x00000, and continues to the beginning of Banked memory, which then continues to the Common 1 memory address space.

Therefore the hardware (or physical mapping) will show that the 256kB Flash is located from 0x00000 to 0x3FFFF, and the 1MB RAM from 0x00000 to 0xFFFFF but the lower quarter of the address space mostly masked by Flash. For programming we’ll have to move this around.

When the Z80 starts up it always begins from physical and logical address 0x0000. Therefore it is typical to put the program storage in the lower address range, and the RAM in the upper range. Given the use of the MMU available in the Z180 we initially can map the SRAM into the upper 32kB of logical address space, using the Common 1 bank and CBAR setting, leaving the first 32kB of Flash in the lower address range.

One difficulty is that there are only 3 memory spaces available, so if we want to have a C stack, global buffers and queues, and global data, then we need to put some SRAM in the Common 0 address space. Let’s uncover 8kB of 1MB SRAM and place this from 0x2000 to 0x3fff to provide this C stack and global variable data frame.

At some stage I assume I’ll want to use all of the additional Flash and SRAM available, so I’ll have to integrate programming for MMU bank switching, and RAM heap/stack switching when the need arises. At least initially there will be a statically programmable range of between 8kB Flash with 56kB SRAM to 56kB Flash with 8kB SRAM available within the logical address space, depending on the MMU initialisation settings.

To program the Z180 the physical memory addresses will need to be juggled around (glue logic) to disable the Flash and SRAM from appearing in the first 8kB Bytes of the address space. The USB programmer will provide program codes in the form of a remote boot-loader to load the contents of programs into the Flash, by moving the entire physical flash through a 4kB or 8kB Banked page. SRAM located in the lower 32kB address range will be loaded with a program to enable buffering and page writing of the desired programs.

Physical Address Range Run Mode Programming Mode
$00000 – $01FFF Flash (8,192B of 256kB) USB (8,192B)
$02000 – $03FFF SRAM (8,192B of 1MB) SRAM (516,096B of 1MB)
$04000 – $3FFFF Flash (245,760B of 256kB)
$40000 – $7FFFF  SRAM (768kB of 1MB)
$80000 – $BFFFF Flash (256kB of 256kB)
$C0000 – $FFFFF SRAM (256kB of 1MB)
Logical Address Range Run Mode Programming Mode
$0000 – $1FFF Flash (8,192B, Common 0) USB (8,192B)
$2000 – $3FFF SRAM (8,192B, Common 0) SRAM (24,576B, Common 0)
$4000 – $5FFF Flash (8,192B, Common 0)
$6000 – $7FFF Flash (8,192B, Banked)
$8000 – $FFFF SRAM (32,768B, Common 1) Flash (32,768B, Banked)

The programming mode (address mapping) will be entered by either a button press, or by signalling from the USB – USART interface lines. The process is to invert the Address 19 line, shifting the physical address location of Flash, and mute Address 0-12 to prevent memory being read from these locations, which allows the USB – USART FIFO to provide program opcodes. Mute by disabling the CE on both Flash and SRAM when A13 through A19 are 0. Then configure the MMU to bring the Flash into Banked logical addresses, using the SRAM to buffer 4kB page writes to Flash memory.

Using the ATMEL WinCUPL tool, it is pretty straightforward to convert the above memory mapping and below logic mapping in CUPL language description to JEDEC format that can be handled by a MiniPro TL866 EEPROM & PLD Programming tool, and programmed into an ATF16V8B device.


Memory and IO Pin Definitions



Memory and IO Pin Logic

This memory and IO mapping needs to be augmented by a secondary logic mapping for managing programming, single step, and other functions, which will be programmed into a second ATF16V8B device. The logic mapping will allow automatic programming initiation via the FT245R USB interface.


Logic Pin Definitions


Logic Pin Logic

Using EEPLD devices will save significant PCB real estate, and will allow me to compensate for minor logic errors after the fact.

FCPU selection

The best flash we can get easily is 55ns timing. This is bettered by SRAM, with 45ns. Converting this timing to a bus frequency we can achieve 20MHz or slightly better, but allowing for some buffering or address logic delay it would be better to keep the system bus under 20MHz.

Using this system bus speed of approximately 20MHz then poses a question; which is the right speed? Some references point out that the Z180 is very poor at holding the correct USART rate when the CPU clock is not a magic multiple of the USART rate. This is the same issue that the AVR ATmega device faces when its USART is not driven by a magic frequency clock. Therefore, lets us set  the CPU clock to 18.432MHz, being most appropriate magic frequency for the following design.

The Z180 crystal clock can generate either doubled, or halved system clocks from the base rate. Starting with 18.432MHZ CPU clock, the system bus clock can be halved, and operated at 9.216MHz as needed. This is slow enough to allow most peripherals to interact with the CPU. Internally, the clock can be doubled to operate the CPU at 36.864MHz. This rate is slightly overclocking the Z180, but that’s what we live for. We don’t build slow computers.

I/O Mapping

I have been thinking about the whole idea of system modularity. Actually, I don’t think the traditional method of building a backplane is such a good idea for what I want to achieve. Extending the address bus a long distance means that I’ll be investing in design and timing issues, that I’m not really sure I know how to solve. So, let’s focus on a smaller design, with on just one board for the time being.

As a computer always needs to be extended and interact with the real world, I think it would be good to add modern user interfaces to the solution. Use Address 15-13 to provide I/O selection options on the CPU Board, using the remaining output pins from the Memory ATF16V8B. This will allow flexibility to latch data into the Hex Display, or trigger breakpoints using #M1 and #Wait to allow Single Step execution from any code point.

I/O Address Range Chip Select (A15,A14,A13) Device
$0000 – $1FFF DO NOT USE ($o, b000) Internal I/O
A7 – A0
Internal INT
$0000 – $00FF Registers
$2000 – $3FFF BREAK ($1, b001) Break Point
Toggle Single Step Mode
$4000 – $5FFF #DIO_CS ($2, b010) PIO 82C55
A1 – A0
$4000 – $4003 Registers
$6000 – $7FFF EXPANSION ($3, b011) Hold for Expansion
$8000 – $9FFF #I2C_CS2 ($4, b100) PCA9665
A1 – A0
$8000 – $8003 Registers
$A000 – $BFFF #I2C_CS1 ($5, b101) PCA9665
A1 – A0
$A000 – $A003 Registers
$C000 – $DFFF #FPU_CS ($6, b110) FPU_CS – Am9511A-1
A0 & #WAIT
$C000 – $C001 Registers
$E000 – $FFFF EXPANSION ($7, b111) Hold for Expansion


Included I/O features & BOM




Single Step – The Z-80’s #M1 pin is useful for building logic to single-instruction step the machine. You do this using the memory ready signal on #M1 to clear a 7474 flip-flop, which is clocked by a Single Step signal, to produce a #WAIT signal for the CPU. That stops the machine on the opcode fetch cycle with the address showing on the address bus and the opcode byte showing on the data bus. To move the machine ahead, you clock the flip-flop which releases the #WAIT signal, until the next #M1 clears the 7474  again.


Memory & Addressing Logic Glue

  • Programmable Logic PLD – Digikey ATF16V8B

USB – Flash programming interface

  • USB-Parallel Bus FTDI245RQ
    Note that  the Write strobe is confusing, but assume ACTIVE LOW.

USB – USART interface

  • USB-Serial FTDI232RQ

Hex 7 Segment Display – 5x Address Digits – 2x Data Digits

  • Decoder DM9368 – Digikey DM9368N-ND
  • LED Display VDMY10C0 & VDMG10C0 – Digikey VDMY10C0TR-ND
  • OR
  • LED Display with Decoder TIL311A – eBay only.

General Digital Input / Output – Being able to read and write simple digital levels is an important thing. So let’s include the 82c55 PIO device.

  • Intel CMOS 82C55 Programmable IO CP82C55A – Digikey CS82C55AZ-ND

I2C interface – This is the most important interface, which provides many extension options, and a plethora of Grove System sensors. Unfortunately the 5V nature of the system precludes using the newest really fast, deep buffer, devices with multiple bus I2C 1MHz bus interfaces, so provide 2x devices to support different applications (eg. video output and sensor acquisition on separate buses). Use the #INT1 & #INT2 interrupts.

Floating Point Processor – This lovely old chip is just too slow to contribute, but still we’ll build it in. Need to provide a 3MHz clock to drive it (FFPU = FCPU/6), and connect its #PAUSE to #WAIT. #END connected to #INT0. RESET is ACTIVE HIGH.


Am9551A-1 3MHz FPU

Bus interface

  • Address & Control – Octal Buffer Driver sn74abt541b – Digikey 296-14668-1-ND
  • Data – Octal Bus Transceiver sn74abth245 – Digikey 296-4140-1-ND

Power Supplies

Excluded I/O features & BOM

Ethernet – Wiznet W5300 Direct address mode requires 3FF of address space. Configure the #INT0 interrupt and implement the DMAC1 I/O to move packets quickly. Exclude this from the initial build, as it is quite a complicated 100 pin device, and it needs 3.3V supply.

USB & SPI interface – provide mass storage capability using either USB or SPI interface devices. The CH37x series is not very well documented, or readily available. There are other options for I2C-SPI bridging which can provide an SPI interface if needed.

  • CH376S – Incorporating the USB functions as described in CH375, and CH372

ADC interface – Better, higher resolution, faster chips are available with I2C interfaces.

  • 8 bit ADC with internal reference MAX158 – Digikey MAX158ACPI+-ND

Video Interface – Can be done using an external I2C device.

  • LCD Video – via I2C with a FTDI EVE

Super I/O – Floppy Disk & IDE Controller – Too hard and doesn’t bring much value to the table. Floppy drives are hard to find, and there are already 2x USART ports available on the Z8S180.

Software & References

Z80 Info

z88dk Development Kit

Small Device C Compiler

SASM Softools – Z180 (MMU aware) C compiler. Commercial Licence.

Programming the Z80 by Rodnay Zaks

The Undocumented Z80

Logic Families

FreeRTOS 9.0.0 for eZ80

Hardware Design notes

Well quite a few iterations on my aspirations and thoughts have resulted in a YAZ180_v1 schematic, that I think is now basically frozen. Some of my design decisions follow.

After deciding not to use the TIL311 LED display devices because they are not easily obtainable, I found the alternative solution using the DM9368 display driver chip and LED display devices was very consuming of space on the board. Since the board had to be no greater than a maximum of 10cm by 16cm I decided to buy some TIL311 devices in advance, and then with the comfort of stock in hand I could use them for the design. Also, they add significantly to the retro-chique.

I would have loved to add the Wiznet W5300 device to the design to provide high speed Ethernet capability, but with a 100 pin VLSI it was just too complicated for the initial design. Next time.

I added a 82c55 interface chip. Knowing that it is very slow, requiring wait states to drive it, is one thing. But the advantage of having multiple latchable input and output ports, off one fairly compact integrated device made up for that problem. As it is a 1970s device it is also retro-chique accretive.

Designing using CUPL and the Atmel PLDs is in my opinion much simpler than tracing schematics and thinking through 7400 series NOR, NAND gates to get the desired addressing logic. Writing the logic in “c” like syntax is much less taxing on the brain.

After deciding to implement no bus drivers, and then building a solution adding drivers and termination to every address and data line, I have rationalised down to bus drivers on data, lower address, and control lines. These are the few lines that appear on all memory and I/O devices, and are taken to every point on the board. The upper address lines only appear on the PLD, SRAM, and Flash, which are very low load modern devices, so there is no point to buffer them. Using ABT logic limits the differential delay on the address lines to 2 ns, and since the read and write select lines are also buffered, there is no issue timing issue created.

And here is the first schematic, in pdf YAZ180_V1. Errors and omissions are possible.

Hardware Layout notes

With the first major layout session behind me, I have the following notes.


Address & Data to SRAM and FLASH

Change the SN74ABT244 for the SN74ABT541. The only difference is the pin ordering, with the 244 being optimal for counter-flowing signals, and the 541 being optimal for unidirectional signals (physically). This will help layout.

Getting 20 address lines and 8 data lines to appear on the SMD SRAM memory is challenging. I will probably reassign the address lines to suit the Z8S180 pin-out, rather than spending hours untangling the lines. It won’t be too easy to do this with the Flash device, but as it is in a PLCC through-hole socket it comes inherently with “vias” making reaching it half as hard as SMD.

Input pin and I/O pins on Z8S180 include weak latch circuits to prevent excessive current draw by the receiver, if the pin is not externally driven. External pull-up or pull-down resistors should not be less than 15 kOhms to ensure that the resistors can control the state of the latch when power is supplied.


All Address and Data lines routed.



A few more air-wires remain, that will need thought.

Following about 10 hours of juggling, just two wires remain together with a plan to resolve them tomorrow.


Just two air-wires remain.

Finished, except for the detailed checking, which usually results in some changes. But, it is done.

The screen grabs below show with the Vcc Layer in Grey, and GND Layer in Green. I have added some basic traces on those layers to close off the routing, which are misleading. The layers are actually completely filled, normally.

This slideshow requires JavaScript.

Bill of Materials

Well, building this retro-computing machine is not quite as cheap as an ATMEL micro-controller board. But then again, it is not quite in the same league, with 1MB of RAM, and 256kB of Flash storage, together with a FPU and 9 digits of LED display.

I have ordered all of the components, except the Am9511A-1 FPU and the TIL311 LED display devices, from Digikey. The BOM is detailed in this link. To add to BOM, this there are the Am9511A-1 and the TIL311 devices which can be found on eBay or other auction sites.




And, the PCB looks great!

The build and bringing up of the board will be on the next story, once I get the new SMD oven sorted out.

Freewrite – First Thoughts

Having taken delivery of a beautifully sculpted object, the Freewrite, I believe it was worth the wait, and the money, in every way. I’ve built both hardware and software, and I know that it is unbelievably difficult to get a piece of hardware (including aluminium, plastic, PCB, components, assembly, testing, and the list of items goes on) constructed and shipped globally. Rightly, getting this construction process completed has been the focus of the Astrohaus team over the past year.

freewrite crop

During the many months of waiting for delivery of my Freewrite, I have purchased Cherry mechanical keyboards from both WASD and Das Keyboard (costing US$150 and more), and I use them during my day to day work and pleasure. There is nothing even remotely close to the pleasure of having Cherry MX switches under your fingertips (I prefer Clear and Brown varieties).

These mechanical keyboards can work with a Chromebook (costing US$300 or more) to produce a functionality similar to the Freewrite. But, then there are two pieces to be transported, and the whole package is not a single writing tool. Spending the time to juggle the pieces of the drafting package leads to further distractions, and the result is not substantially cheaper than the Freewrite.

The Astrohaus software game has been mediocre, as this article notes. Hopefully, getting the hardware out the door will allow them to focus on the many minor software shortcomings. As it is just software, it should be easy for them to provide OTA upgrades of the Freewrite to remedy the situation.

Arduino FreeRTOS

Arduino FreeRTOS Logo

For a long time I have been using the AVR port of FreeRTOS as the platform for my Arduino hardware habit. I’ve written (acquired, stolen, and corrupted) a plethora of different drivers and solutions for the various projects I’ve built over the last years. But, sometimes it would be nice to just try out a new piece of hardware in a solid multi-tasking environment without having to dive into the datasheets and write code. Also, when time is of the essence rewriting someone’s existing driver is just asking for stress and failure.

So recently, with an important hack-a-thon coming up, I thought it would be nice to build a robust FreeRTOS implementation that can just shim into the Arduino IDE and allow me to use the best parts of both environments, seamlessly.

Arduino IDE Core is just AVR

One of the good things about the Arduino core environment is that it is just the normal AVR environment with a simple Java IDE added. That means that all of the AVR command line tools used to build Arduino sketches will also just work my AVR port of FreeRTOS.

Some key aspects of the AVR FreeRTOS port have been adjusted to create the seamless integration with the Arduino IDE. These optimizations are not necessarily the best use of FreeRTOS, but they make the integration much easier.

FreeRTOS needs to have an interrupt timer to trigger the scheduler to check which task should be using the CPU, and to fairly distribute processing time among equivalent priority tasks. In the case of the Arduino environment all of the normal timers are configured in advance, and therefore are not available for use as the system_tick timer. However, all AVR ATmega devices have a watchdog timer which is driven by an independent 128kHz internal oscillator. Arduino doesn’t configure the watchdog timer, and conveniently the watchdog configuration is identical across the entire ATmega range. That means that the entire range of classic AVR based Arduino boards can be supported within FreeRTOS with one system_tick configuration.

The Arduino environment has only two entry point functions available for the user, setup() and loop(). These functions are written into an .ino file and are linked together with and into a main() function present in the Arduino libraries. The presence of a fixed main() function within the Arduino libraries makes it really easy to shim FreeRTOS into the environment.

The main() function in the main.c file contains a initVariant() weak attribute stub function prior to the internal Arduino initialisation setup() function. By implementing an initVariant() function execution can be diverted into the FreeRTOS environment, after calling the normal setup() initialisation, by simply continuing to start the FreeRTOS scheduler.

int main(void) // Normal Arduino main.cpp. Normal execution order.
    initVariant();  // Our initVariant() diverts execution from here.
    setup();  // The Arduino setup() function.

    for (;;)
        loop();  // The Arduino loop() function.
        if (serialEventRun) serialEventRun();
    return 0;

Firstly, this initVariant() function is located in the variantHooks.cpp file in the FreeRTOS library. It replaces the weak attribute function definition in the Arduino core.

void initVariant(void)
    setup();  // The Arduino setup() function.
    vTaskStartScheduler();  // Initialise and run the FreeRTOS scheduler. Execution should never return to here.
    vApplicationMallocFailedHook();  // Possibly we've failed trying to initialise heap for the scheduler. Let someone know.

Secondly, the FreeRTOS idle task is used to run the loop() function whenever there is no unblocked FreeRTOS task available to run. In the trivial case, where there are no configured FreeRTOS tasks, the loop() function will be run exactly as normal, with the exception that a short scheduler interrupt will occur every 15 milli-seconds (configurable). This function is located in the variantHooks.cpp file in the library.

void vApplicationIdleHook( void )
    loop();  // The Arduino loop() function.
    if (serialEventRun) serialEventRun();

Putting these small changes into the Arduino IDE, together with a single directory containing the necessary FreeRTOS v8.2.3 files configured for AVR, is all that needs to be done to slide the FreeRTOS shim under the Arduino environment.

I have published the relevant files on Github where the commits can be browsed and the repository downloaded. The simpler solution is to install FreeRTOS using the Arduino Library Manager, or download the ZIP files from Github and install manually as a library in your Arduino IDE.

Getting Started with FreeRTOS

Ok, with these simple additions to the Arduino IDE via a normal Arduino library, we can get started.

Firstly in the Arduino IDE Library manager, from Version 1.6.8, look for the FreeRTOS library under the Type: “Contributed” and the Topic: “Timing”.

Arduino Library Manager

Arduino Library Manager

Ensure that the most recent FreeRTOS library is installed. As of writing that is v8.2.3-16.

FreeRTOS v8.2.3-6 Installed

Example of FreeRTOS v8.2.3-6 Installed

Then under the Sketch->Include Library menu, ensure that the FreeRTOS library is included in your sketch. A new empty sketch will look like this.


Compile and upload this empty sketch. This will show you how much of your flash is consumed by the FreeRTOS scheduler. As a guide the following information was compiled using Arduino v1.6.9 on Windows 10.

// Device:   loop() -> FreeRTOS | Additional Program Storage
// Uno:         444 ->   7340   |     21%
// Goldilocks:  502 ->   7408   |      6%
// Leonardo:   3624 ->  10508   |     24%
// Yun:        3618 ->  10502   |     24%
// Mega:        656 ->  24108   |      9%

Now test and upload the Blink sketch, with an underlying Real-Time Operating System. That’s all there is to having FreeRTOS running in your sketches. So simple.

Next Steps

Blink_AnalogRead.ino is a good way to take the next step as it combines two basic Arduino examples, Blink and AnalogRead into one sketch with in two separate tasks. Both tasks perform their duties, managed by the FreeRTOS scheduler.

#include <Arduino_FreeRTOS.h>

// define two tasks for Blink and AnalogRead
void TaskBlink( void *pvParameters );
void TaskAnalogRead( void *pvParameters );

// the setup function runs once when you press reset or power the board
void setup() {

  // Now set up two tasks to run independently.
    ,  (const portCHAR *) "Blink";   // A name just for humans
    ,  128  // This stack size can be checked and adjusted by reading the Stack Highwater
    ,  NULL
    ,  2  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,  NULL );

    ,  (const portCHAR *) "AnalogRead";
    ,  128  // Stack size
    ,  NULL
    ,  1  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,  NULL );

  // Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started.

void loop()
  // Empty. Things are done in Tasks.

/*---------------------- Tasks ---------------------*/

void TaskBlink(void *pvParameters)  // This is a task.
  (void) pvParameters;

  // initialize digital pin 13 as an output.
  pinMode(13, OUTPUT);

  for (;;) // A Task shall never return or exit.
    digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
    vTaskDelay( 1000 / portTICK_PERIOD_MS ); // wait for one second
    digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
    vTaskDelay( 1000 / portTICK_PERIOD_MS ); // wait for one second

void TaskAnalogRead(void *pvParameters)  // This is a task.
  (void) pvParameters;

  // initialize serial communication at 9600 bits per second:

  for (;;)
    // read the input on analog pin 0:
    int sensorValue = analogRead(A0);
    // print out the value you read:
    vTaskDelay(1);  // one tick delay (15ms) in between reads for stability

Next there are a number of examples in the FreeRTOS Quick Start Guide.

One last important thing you can do is to reduce device power consumption by not using the default loop() function for anything more than putting the MCU to sleep. This code below can be used for simply putting the MCU into a sleep mode of your choice, while no tasks are unblocked. Remember that the loop() function shouldn’t ever disable interrupts and block processing.

#include <avr/sleep.h>  // include the Arduino (AVR) sleep functions.

loop() // Remember that loop() is simply the FreeRTOS idle task. Something to do, when there's nothing else to do.
// Digital Input Disable on Analogue Pins
// When this bit is written logic one, the digital input buffer on the corresponding ADC pin is disabled.
// The corresponding PIN Register bit will always read as zero when this bit is set. When an
// analogue signal is applied to the ADC7..0 pin and the digital input from this pin is not needed, this
// bit should be written logic one to reduce power consumption in the digital input buffer.

#if defined(__AVR_ATmega640__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) // Mega with 2560
DIDR0 = 0xFF;
DIDR2 = 0xFF;
#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega1284PA__) // Goldilocks with 1284p
DIDR0 = 0xFF;

#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) // assume we're using an Arduino with 328p
DIDR0 = 0x3F;

#elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) // assume we're using an Arduino Leonardo with 32u4
DIDR0 = 0xF3;
DIDR2 = 0x3F;

// Analogue Comparator Disable
// When the ACD bit is written logic one, the power to the Analogue Comparator is switched off.
// This bit can be set at any time to turn off the Analogue Comparator.
// This will reduce power consumption in Active and Idle mode.
// When changing the ACD bit, the Analogue Comparator Interrupt must be disabled by clearing the ACIE bit in ACSR.
// Otherwise an interrupt can occur when the ACD bit is changed.

// There are several macros provided in the header file to actually put
// the device into sleep mode.

set_sleep_mode( SLEEP_MODE_IDLE );


// Only if there is support to disable the brown-out detection.
#if defined(BODS) && defined(BODSE)

sleep_cpu(); // good night.

// Ugh. I've been woken up. Better disable sleep mode.
sleep_reset(); // sleep_reset is faster than sleep_disable() because it clears all sleep_mode() bits.

o that’s all there is to it. There’s nothing more to do except to read the FreeRTOS Quick Start Guide.
Further reading with manicbug, and by searching on this site too.

General Usage

FreeRTOS has a multitude of configuration options, which can be specified from within the FreeRTOSConfig.h file. To keep commonality with all of the Arduino hardware options, some sensible defaults have been selected.

The AVR Watchdog Timer is used with to generate 15ms time slices, but Tasks that finish before their allocated time will hand execution back to the Scheduler. This does not affect the use of any of the normal Timer functions in Arduino.

Time slices can be selected from 15ms up to 500ms. Slower time slicing can allow the Arduino MCU to sleep for longer, without the complexity of a Tickless idle.

Watchdog period options:

  • WDTO_15MS
  • WDTO_30MS
  • WDTO_60MS
  • WDTO_120MS
  • WDTO_250MS
  • WDTO_500MS

Note that Timer resolution is affected by integer math division and the time slice selected. Trying to accurately measure 100ms, using a 60ms time slice for example, won’t work.

Stack for the loop() function has been set at 128 bytes. This can be configured by adjusting the configIDLE_STACK_SIZE parameter. It should not be less than the configMINIMAL_STACK_SIZE. If you have stack overflow issues, just increase it. Users should prefer to allocate larger structures, arrays, or buffers using pvPortMalloc(), rather than defining them locally on the stack. Or, just declare them as global variables.

Memory for the heap is allocated by the normal malloc() function, wrapped by pvPortMalloc(). This option has been selected because it is automatically adjusted to use the capabilities of each device. Other heap allocation schemes are supported by FreeRTOS, and they can used with additional configuration.


  • Stack Overflow: If any stack (for the loop() or) for any Task overflows, there will be a slow LED blink, with 4 second cycle.
  • Heap Overflow: If any Task tries to allocate memory and that allocation fails, there will be a fast LED blink, with 100 millisecond cycle.


  • ATmega328 @ 16MHz : Arduino UNO, Arduino Duemilanove, Arduino Diecimila, etc.
  • ATmega328 @ 16MHz : Adafruit Pro Trinket 5V, Adafruit Metro 328, Adafruit Metro Mini
  • ATmega328 @ 16MHz : Seeed Studio Stalker
  • ATmega328 @ 16MHz : Freetronics Eleven, Freetronics 2010
  • ATmega328 @ 12MHz : Adafruit Pro Trinket 3V
  • ATmega32u4 @ 16MHz : Arduino Leonardo, Arduino Micro, Arduino Yun, Teensy 2.0
  • ATmega32u4 @ 8MHz : Adafruit Flora, Bluefruit Micro
  • ATmega1284p @ 20MHz : Freetronics Goldilocks V1
  • ATmega1284p @ 24.576MHz : Seeed Studio Goldilocks V2, Seeed Studio Goldilocks Analogue
  • ATmega2560 @ 16MHz : Arduino Mega, Arduino ADK
  • ATmega2560 @ 16MHz : Freetronics EtherMega
  • ATmega2560 @ 16MHz : Seeed Studio ADK
  • ATmegaXXXX @ XXMHz : Anything with an ATmega MCU, really.

Files and Configuration

  • Arduino_FreeRTOS.h : Must always be #include first. It references other configuration files, and sets defaults where necessary.
  • FreeRTOSConfig.h : Contains a multitude of API and environment configurations.
  • FreeRTOSVariant.h : Contains the AVR specific configurations for this port of FreeRTOS.
  • heap_3.c : Contains the heap allocation scheme based on malloc(). Other schemes are available and can be substituted (heap_1.c, heap_2.c, heap_4.c, and heap_5.c) to get a smaller binary file, but they depend on user configuration for specific MCU choice.

Goldilocks Analogue – Wrap Up

This is the final item on the Goldilocks Analogue as a design and production exercise.

Thank you for pledging on the Kickstarter Project page. Closed on November 19th 2015, with 124% funding. Now that the Kickstarter Pledges have been shipped, the Goldilocks Analogue is available on Tindie.

I sell on Tindie

I’ve been updating this post with the pre-production and production experience over the past few months.

The pre-production design materials, correcting the errata noted on Prototype Version 4, have been finalised and sent to Seeed Studio.

The interim backer report is out, and now manufacturing quantities for procuring parts ready for the February production run have been finalised.

An updated version of the Goldilocks Analogue User Manual is available, and the Production Testing Document has reached Version 2 following inclusion of Windows 10 test procedures.

Revised Arduino IDE Variant files for Goldilocks Analogue using the Arduino core are available on Github.

Also additional optional libraries to provide support for each of the advanced features of the Goldilocks Analogue are available in the Arduino IDE Library Manager.

Arduino IDE compatibility testing revealed only a few remaining issues related to support of the ATmega1284p used in the Goldilocks Analogue. Two issues have been raised and resolved as 2 pull requests on the main Arduino IDE development path.

Both these issues have been committed into the Arduino main git tree, and they have landed in Arduino IDE Release 1.6.8.

The only remaining known issue is the limitation in the configuration of the Tones() code to use only Timer 2. We would like to use Timer 2 for the RTC. There is no other option but to use this Timer for the RTC support, so it would be good if Tones() could be configured to use a different timer.


Rather than going back over old ground, I’ll just be testing the pre-production version against the Version Prototype 4, to ensure that the things that should have improved, are improved, and that nothing has become broken.

Power Supplies

In the image below, Channel 1 (yellow) is 4.47mV of noise present at the output capacitor for the power supply, and Channel 2 (blue) is the 3.47mV supply noise present on a test Vcc pin closest to the MCU.

The significant improvement in noise level for the pre-production version at the MCU is similar to that achieved for the Prototype 4 (even slightly better), and this is probably due to reduced capacitive coupling into the ground plane by removing the ground copper from directly under the main supply inductor.

GA PP Power Supply Noise

GA PP: 5V Power Supply Noise

Remembering, for context, that 4mV is still the same order of the least significant bit for a 5V 10 bit ADC sampler, as found in the ATmega1284p, and a one bit change in the LSB of the MCP4822 input generates a 1mV change in output.

Checking the other power supplies on the board, Channel 1 (yellow) is the 3.3V positive supply, provided by a linear regulator. This supply is not used for analogue components, so the 4.0mV noise level is not critical, but never the less it is slightly less than on the Version P4.

Channel 2 (blue) below shows the -3V supply for the Operational Amplifier. This shows that the supply voltage noise of 5.9mV after filtering by a first order LC filter further smooth this supply. Compared to the Version P4 with no filtering (shown below) the noise is reduced substantially. The Version P4 shows a 10mV ramp, because it is a capacitive charge switching device. The addition of this LC filter was the one substantial change from the Prototype 4, so it is good to see the positive effect on the negative supply input to the Op Amp.

GA PP 3.3V and -3V Power Supply Noise

GA PP: 3.3V and -3V Power Supply Noise

Goldilocks Analogue Prototype 4 - 3.3V & -3V Supply Noise

GA P4: 3.3V & -3V Power Supply Noise

Analogue Output

The standard test that I’ve been using throughout the development is to feed in a 43.1Hz Sine wave generated from a 1024 value 16 bit LUT. The sampling rate is 44.1kHz, which is generated by Timer 1 to get the closest match.

The spectra and oscilloscope charts below can be directly compared to the testing done with prototype Version P4 and earlier versions of the Goldilocks Analogue.

The below chart shows the sine wave generated at the output of the Op Amp. This is exactly as we would like to see, with no compression of either the 4.096V peak, or the 0V trough.

Goldilocks Analogue – 43Hz Sine Wave – Two Channels – One Channel Inverted

GA PP: 43Hz Sine Wave – Two Channels – One Channel Inverted

Looking at the spectra generated up to 953Hz it is possible to see harmonics from the Sine Wave, and other low frequency noise.

The spectrum produced by the Goldilocks Analogue shows most distortion is below -70dB, and that the noise floor lies below -100dB. The pre-production sample shows slightly higher noise carriers than the Version P4, but the difference is not substantial.

GA PP 953Hz

GA PP: 43.1Hz Sine Wave – 953Hz Spectrum

In the spectrum out to 7.6kHz we are looking at the clearly audible range, which is the main use case for the device.

The Goldilocks Analogue has noise carriers out to around 4.5kHz, but they are all below -80dB. After 4.5kHz the only noise remains below -100dB.

GA PP 7k6Hz

GA PP: 43.1Hz Sine Wave – 7k6Hz Spectrum

The spectra out to 61kHz should show a noise carrier generated by the reconstruction frequency of 44.1kHz.

The Goldilocks Analogue shows the spectrum maintains is low noise level below -90dB right out to the end of the audible range, and further out to the reconstruction carrier at 44.1kHz.

GA PP 61kHz

GA PP: 43.1Hz Sine Wave –  61kHz Spectrum

The final spectrum shows the signal out to 976kHz. We’d normally expect to simply see the noise floor, beyond the 44.1kHz reconstruction carrier noise.

The Goldilocks Analogue has a noise carrier at around 210kHz, probably generated by the -3V supply. The noise carrier at 340kHz is generated through the 5V SMPS supply, and is absent when powered by USB socket. Aside from the two carriers mentioned, there is no further noise out to 976kHz.

GA PP 976kHz

GA PP: 43.1Hz Sine Wave – 976kHz Spectrum

The Pre-production analogue output works as specified, and is essentially identical to the analogue output on the Prototype 4. It can maintain the 72dB SNR required, of which it should theoretically be capable.