Cocoacrumbs

Cocoacrumbs

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

Cocoacrumbs

19 minutes read

Pic 1

Intro

Since quite some time now, I’ve been intrigued by the Zilog eZ80 which is a successor of the well known Z80. While the eZ80 remains binary compatible with the Z80 it does have some interesting extensions like a flat 24 bits address space (no ugly 64k segments!) allowing to address up to 16 MByte. More info can be found on this Wikipedia page.

If you search a bit, then you can quite a few hobby projects using the eZ80. For me, the most interesting design was the Z20X open source computing system by Konstantin Dimitrov. This design was also featured on Hackaday. When PCB’s became available on Tindie as well, I had no excuse anymore for not starting a eZ80 project (well I was still busy with my 6845 based terminal of course but shiny new project …).

Shiny new project temptation...

What do we need

Hardware

Next to the PCB(s) and it’s components, we need something to program the eZ80. The Z20X PCB already contains a connector for the ZDI (Zilog Debug Interface) interface. So we need something that allows us to connect our development PC with that ZDI connector. Zilog provides their USB Smart Cable for this. The link goes to Mouser but you can find/buy this in a lot of places. The manual for this cable can be found e.g. here.

Software

To write/compile/upload code for the eZ80, we need the ZDS II (Zilog Development Studio). Luckily for us, this is a free download and doesn’t even need an account. ZDS II v5.3.4 seems to be the latest version.

While you’re downloading, you’d best download all the (relevant) documentation from there as well. Click on eZ80 to see all the documentation.

Setting up a simple ‘Hello World’ project

Now that we have everything that’s needed (I’ll not go into how to install ZDS II and the driver for the USB Smart Cable), let’s start with our first project.

Since the Z20X doesn’t have a LED to build a BlinkenLight project we will have to be a bit more ambitious and build a Hello World project using a serial port (UART0 in our case).

Create a z20x-HelloWorld folder somewhere convenient on your development PC.

Empty z20x-HelloWorld project folder.

Start up the ZDS II and under the File menu, select New Project. We see the kind of dialog box as shown below. For File name, we now specify the name of our project. I choose z20x-HelloWorld for this project. This will create a z20x-HelloWorld.zdsproj file in the z20x-HelloWorld folder.

Creating a new project.

Clicking on Select brings us to the next dialog where, for this simple project, we only need to change the CPU to the eZ80F92 as used in the Z20X:

Selecting the correct CPU.

Clicking on the Continue button brings us to the Step 1 - Build options dialog box as shown below. Nothing needs to be changed here and we can immediately press the Next button.

Nothing to change here.

We arrive now in the Step 2 - Target and Debug Tool Selection dialog box as shown below and now it becomes a bit more complex.

The first thing to do is setting the Debug Tool. Make sure this is set to USBSmartCable.

The Use Page Erase Before Flashing check box at the top of the dialog can be left checked. When checked, the internal Flash memory of the eZ80 will be page erased. When unchecked, the internal Flash is configured to be mass-erased when the code is downloaded.

We also see 4 targets which you should consider as a template to start your own project from. From the list of templates, I select the generic eZ80F92_99C0873_Flash target to start from. Then I hit the Copy button to create my own copy of this generic template.

Selecting the generic `eZ80F92_99C0873_Flash` template.

Once we hit the Copy button we’re presented the dialog as shown below. We only need to specify the name we wish to give the new target which will be a copy of the eZ80F92_99C0873_Flash target. In this case, I choose the name eZ80F92_Flash.

Copying the eZ80F92_99C0873_Flash target to create a new eZ80F92_Flash target.

Hitting the OK button brings us back to the previous dialog box. We now see 5 targets (instead of 4 previously) because our new eZ80F92_Flash target has been added to the list.

Select the eZ80F92_Flash target and check its check box as well to indicate we want to use this target.

Selecting the eZ80F92_Flash target.

We’re not done yet with this dialog box. We now need to set up the target as well. For this, hit the Setup button (while making sure the eZ80F92_Flash target is selected) and we’re presented with a new dialog box as shown below.

Here we need to make quite a few modifications. The dialog box has 6 sub boxes. I’ll go everyone but not in a clock wise order:

  • External RAM:
    • Here we can specify where the external RAM range. I did choose a range from 0x400000 to 0x47FFFF for the 0.5 MByte external RAM chip on the Z20X.
    • This external RAM range specifies a range of RAM available to use as “scratch-pad” memory by ZDS for internal Flash memory page erase. The contents of the external RAM range is first saved and then restored again after a Flash erase operation.
    • Note: If external RAM is not available, the external RAM range must be set to the internal RAM address range of the eZ80.
  • External Memory/IO:
    • For this simple example, we only need to configure the external RAM chip which is selected by CS0 in the Z20X computer.
    • We specify its lower and upper bounds as 0x400000 and 0x47FFFF.
    • Control Register:
      • This register controls the wait states for the memory region when the selected chip select is active. This determined by bits [7:5]:
        • 000 0 WAIT states are asserted when this Chip Select is active.
        • 001 1 WAIT state is asserted when this Chip Select is active.
        • 010 2 WAIT states are asserted when this Chip Select is active.
        • 011 3 WAIT states are asserted when this Chip Select is active.
        • 100 4 WAIT states are asserted when this Chip Select is active.
        • 101 5 WAIT states are asserted when this Chip Select is active.
        • 110 6 WAIT states are asserted when this Chip Select is active.
        • 111 7 WAIT states are asserted when this Chip Select is active.
      • Bit 4 determines if the chip select is configured as a memory select or as a I/O select:
        • 0 Chip Select is configured as a Memory Chip Select.
        • 1 Chip Select is configured as an I/O Chip Select.
      • Bit 3 enables/disables the chip select:
        • 0 Chip Select is disabled.
        • 1 Chip Select is enabled.
      • Bits [2:0] are reserved for future use.
      • We want to enable the Chip Select and use 0 Wait States for our external memory which gives us the hex value 08.
    • Bus Mode:
      • Bits [7:6] BUS_MODE
        • 00 eZ80® bus mode.
        • 01 Z80 bus mode.
        • 10 IntelTM bus mode.
        • 11 Motorola bus mode.
      • Bit 5 AD_MUX
        • 0 Separate address and data.
        • 1 Multiplexed address and data appears on data bus DATA[7:0].
      • Bit 4
        • Reserved.
      • Bits [3:0] BUS_CYCLE
        • 0000 Not valid.
        • 0001 Each bus mode state is 1 CPU clock cycle in duration.
        • 0010 Each bus mode state is 2 CPU clock cycles in duration.
        • 0011 Each bus mode state is 3 CPU clock cycles in duration.
        • 0100 Each bus mode state is 4 CPU clock cycles in duration.
        • 0101 Each bus mode state is 5 CPU clock cycles in duration.
        • 0110 Each bus mode state is 6 CPU clock cycles in duration.
        • 0111 Each bus mode state is 7 CPU clock cycles in duration.
        • 1000 Each bus mode state is 8 CPU clock cycles in duration.
        • 1001 Each bus mode state is 9 CPU clock cycles in duration.
        • 1010 Each bus mode state is 10 CPU clock cycles in duration.
        • 1011 Each bus mode state is 11 CPU clock cycles in duration.
        • 1100 Each bus mode state is 12 CPU clock cycles in duration.
        • 1101 Each bus mode state is 13 CPU clock cycles in duration.
        • 1110 Each bus mode state is 14 CPU clock cycles in duration.
        • 1111 Each bus mode state is 15 CPU clock cycles in duration.
      • I choose the eZ80 bus mode and Each bus mode state is 1 CPU clock cycle in duration. Combined this gives us a 0x01 hex value for the Bus Mode register.
  • PC/STACK Registers:
    • For the Program Counter we use 0 since we will position the internal Flash Memory starting from address 0x000000.
    • For the SPL Stack Pointer (which is the stack pointer when we run in 24 bit ADL mode) we choose 0x480000, which is at the end of the external RAM region.
    • For the SPS Stack Pointer (which is the stack pointer when we run in 16 bit Z80 mode), we can keep the default 0xFFFF value.
  • Internal Memory:
    • The eZ80F92 has both 8K Static RAM and 128K Flash on board. Here we can specify where they will be located in its memory map.
    • Internal RAM: We keep the internal RAM enabled and we set the Address Upper Byte to B7 (meaning it will start from address 0xB70000).
    • Flash: Also here we enable the Flash and we set its Address Upper Byte to 00 (meaning it will start from address 0x000000).
      • We can also set the number of Wait States when accessing the internal Flash. When a new project is created, ZDS II will suggest the optimal number of wait states for you assuming you filled in the clock frequency corrected. This can be seen by an ***** appended after the Wait State value in the Drop Down List.
      • The wait states value is based on the value of the system clock frequency according to the following table:
      • Wait States - System Clock (MHz)
        • 0 ............. <12 MHz
        • 1 ............. 12–23.9 MHz
        • 2 ............. 24–35.9 MHz
        • 3 ............. 36–47.9 MHz
        • 4 ............. 48–59.9 MHz
        • 5 ............. 60–71.9 MHz
        • 6 ............. 72–84 MHz
        • 7 ............. >84* MHz
        • 5, 6, and 7 wait states are not recommended for performance reasons.
  • Clock:
    • Here we can specify the frequency of the crystal that is present on the Z20X board. This is important for baud rate calculations. Our example code will calculate the correct value for the baud rate generator register (brg) based on the value we fill in here. I use a 18.432 MHz crystal on my board (instead of the specified 18 MHz in the BOM). See later as well.
  • Mode:
    • Here we specify if the eZ80 in this project starts up in 24 bit ADL mode or in Z80 16 bits addressing mode. We can leave this boxed checked for this simple project.
    • Note: This information is used only by the Debugger and does not affect your code initialization. The actual mode needs to be in your source files.
My target configuration.

We’re not done yet with the above dialog box. We need to further configure the Flash by clicking on the Configure Flash button. We’re then presented with the dialog box as shown below.

  • Internal Flash: Make sure the check box is checked.
  • External Flash: Make sure the check box is unchecked.
Configuring the Flash.

Now we can finally click twice on the OK button (once for the Target Flash Settings dialog and once for the parent Configure Target dialog). And we get back to the Step 2 - Target and Debug Tool Selection and there we can now finally hit the Next button to bring us to the Step 3 - Target Memory Configuration dialog as shown below.

Here we specify the Linker Address Spaces and the Link Configuration:

  • Linker Address Spaces:
    • ROM: Set this to 0x000000-0x01FFFF (corresponds to the location and size of 128K of the internal Flash).
    • RAM: Set this to 0x400000-0x47FFFF (the same values as we had to enter earlier to define the external RAM).
  • Link Configuration:
    • We can keep this to Standard for this simple project.
Filling in the Target Memory Configuration.

And we can finally press the Finish button now and the IDE will present itself.

But we’re still not done yet… We still need to make small changes to the project settings. For this, go to the Project menu and select Settings.

Opening the Project Settings.

We’re now presented the dialog below (showing the ZSL pane - after clicking on the ZSL on the left).

Our Hello World project will use UART0 which is Port D of the eZ80 GPIO ports. So, in the Ports box, we check Port D and in the Uarts box, we check UART0:

Enabling UART0 in the project.

Now we’re finally done with setting up the project and we can start adding some code!

And this is the code for this project:

/*****************************************************************************
 *  This program will exercise the eZ80Acclaim! UART.  The UART will be used
 *  to print a few messages and echo any character it receives from the
 *  Console port.  The target specific information is as follows:
 *
 *  Target
 *  ==========================================================================
 *    eZ80F91
 *    ----------------------------------------------
 *      Select Ethernet/Serial/USB in Settings->Debugger from Project menu.
 *      The program output will be sent to UART0.
 *
 *      UART Parameters (default):
 *        57600 bps, 8 bits per character, No parity, 1 stop bit, No flow ctrl
 *
 *    Simulator
 *    ----------------------------------------------
 *      Select Simulator in Settings->Debugger from Project menu. The program
 *      output will be displayed in the Debug tab on the output window.
 *
 *  Project Settings and Build Configurations
 *  ==========================================================================
 *    Debug
 *    ----------------------------------------------
 *      The settings in this configuration allow the program to run on
 *      the Simulator and on the development platform in RAM during a
 *      debug session.
 *
 *    Release
 *    ----------------------------------------------
 *      The settings in this configuration allow the program to reside
 *      and execute from internal Flash in the eZ80F91 on the mini module.
 *
 *  Example Output
 *  ==========================================================================
 *    ZiLOG Developers Studio
 *    i = 5
 *    d = 25
 *    f = 1.260000
 *    eZ80F91 5 25 1.260000
 *
 *   Change History: 
 *   12/02/11 - Fixed UART when ZSL is selected
 *
 ****************************************************************************/
#include <eZ80.h>
#include <stdio.h>

#define UART                 0			// change to 1 when using UART 1
#define UART_BPS             57600
#define UART_DATA_BITS       8
#define UART_PARITY          0
#define UART_STOP_BITS       1

#ifdef _EZ80190
#define DEVICE_NAME          "eZ80190"
#define UZI_MODE_UART        (unsigned char)0x01
#endif
 
#ifdef _EZ80L92
#define DEVICE_NAME          "eZ80L92"
#endif
 
#ifdef _EZ80F93
#define DEVICE_NAME          "eZ80F93"
#endif
 
#ifdef _EZ80F92
#define DEVICE_NAME          "eZ80F92"
#endif
 
#ifdef _EZ80F91
#define DEVICE_NAME          "eZ80F91"
#endif
 
#if !defined(_ZSL_UART_USED)
#if (UART==1) 
#define	UART_FCTL	     	 UART1_FCTL
#define UART_RBR             UART1_RBR
#define UART_THR             UART1_THR
#define UART_BRG_L           UART1_BRG_L
#define UART_BRG_H           UART1_BRG_H
#define UART_LCTL            UART1_LCTL
#define UART_LSR             UART1_LSR
#else
#define	UART_FCTL	     	 UART0_FCTL
#define UART_RBR             UART0_RBR
#define UART_THR             UART0_THR
#define UART_BRG_L           UART0_BRG_L
#define UART_BRG_H           UART0_BRG_H
#define UART_LCTL            UART0_LCTL
#define UART_LSR             UART0_LSR
#endif
 
#define LCTL_DLAB            (unsigned char)0x80
#define LSR_THRE             (unsigned char)0x20
#define LSR_DR               (unsigned char)0x01
 
#define SetLCTL(d, p, s)     UART_LCTL = ((d-5)&3) | (((s-1)&1)<<2) | (p&3)
#endif //!defined(_ZSL_UART_USED)

#define LF                   '\n'
#define CR                   '\r'
 
extern long SysClkFreq;
char device_name[] = DEVICE_NAME;

void uart_init(void) 
{ 
#ifndef _SIMULATE
 #ifdef _ZSL_UART_USED
	ei();
   #ifdef ZSL_DEVICE_UART0
	//enable Max3222 driver
    PB_DR = 0x40;
    PB_ALT2 = 0x00;
    PB_ALT1 = 0x00;
    PB_DDR = 0xBF;
   #endif
 #else
    unsigned short int i;
    unsigned short brg;
    brg = SysClkFreq/(16 * UART_BPS);
 
  #if (UART==1)
    PC_ALT2 = 0x03;
    PC_ALT1 = 0x00;
    PC_DDR = 0xEB;
    PC_DR = 0x00;
  #else
    PD_ALT2 = 0x03;
    PD_ALT1 = 0x00;
    PD_DDR = 0xEB;
    PD_DR = 0x00;
	
	//enable Max3222 driver
    PB_DR = 0x40;
    PB_ALT2 = 0x00;
    PB_ALT1 = 0x00;
    PB_DDR = 0xBF;
  #endif 

    UART_LCTL |= LCTL_DLAB;
    UART_BRG_L = (brg & (unsigned short)0x00FF);
    UART_BRG_H = (brg & (unsigned short)0xFF00) >> (unsigned short)8;
    UART_LCTL &= ~LCTL_DLAB;
    UART_FCTL = 0x07;	// eZ80F91 date codes 0611 and after requires disabling FIFO.
    SetLCTL(UART_DATA_BITS, UART_PARITY, UART_STOP_BITS);
 #endif
#endif 
}

#if defined(_ZSL_UART_USED)
#ifdef ZSL_DEVICE_UART1
/*** Note: As default, ZSL uses UART0 for functions: getch, putch and peekc.
 *         These functions were compiled with UART0 and were parts of ZSL library.
 *         When using UART1, it is required to overwrite functions getch, putch and
 *         peekc if they are used in this project.
 */

INT getch( VOID )
{
	CHAR ch ;
	UCHAR stat = UART_ERR_NONE ;
	UINT nbytes = 1 ;
	UART1_SPR = read_UART1( &ch, &nbytes ) ;
	stat = UART1_SPR ;
	nbytes = (UINT) ch ;
	nbytes = (nbytes & 0x0000FF) ;
	return (UART_ERR_NONE!=stat) ? EOF : nbytes ;

}//! end of

INT putch( INT ich )
{
	CHAR ch[ 2 ] ;
	CHAR cnt = 1 ;
	UCHAR stat = UART_ERR_NONE ;
	ch[ 0 ] = ich ;												//!< Copy the character byte to be transmitted.
	if( '\n' == ch[ 0 ] )										//!< See if this is a new line character.
	{															
		ch[ 1 ] = '\r' ;										//!< Add a carriage-return to this character.
		cnt++ ;													//!< Accommodate this carriage-return character.
	}
	stat = write_UART1( ch, cnt ) ;								//!< Transmit this byte on UART1.
	return stat ;
}
#endif
#else
#if defined(_SIMULATE)
int putch(int ch)
{
    UART_THR = ch;
    return (ch);
}
#else
int putch(int ch)
{
    while ((UART_LSR & LSR_THRE) == (unsigned char)0);
    UART_THR = ch;
    if (ch == LF)
    {
        while ((UART_LSR & LSR_THRE) == (unsigned char)0);
        UART_THR = CR;
    }
    return (ch);
}
#endif

int getch(void)
{
    while ((UART_LSR & LSR_DR) == (unsigned char)0);
    return (UART_RBR);
}
#endif 

void test(void)
{
    int a = 1;
    int b = 2;
    int c = 3;
    c = b + a + 2;
    c++;
    c++;
}

int main()
{
    int ch;
    int i = 0;
    static char zds[] = "ZiLOG Developers Studio";

    uart_init();
    test();
    printf("\nUsing Uart %i\n", UART);
    printf("-----------------------\n");
    printf("%s\n", zds);
    printf("-----------------------\n");
    printf("i = %i\n", 5);
    printf("d = %d\n", 25);
    printf("f = %f\n", 1.26);
    printf("%s %i %d %f\n", device_name, 5, 25, 1.26);
    printf("-----------------------\n");
 
    while (1)
    {   ch = getchar();
        putchar(ch);
        if( '\r' == ch )
        {
			putchar( '\n' ) ;
        }
    }
    return 0;
}

The above code is the same code you can find in the sample code directory which is part of the ZDS II environment you installed.

Save the above code in a file. E.g. as main.c in your project folder.

We can now add the main.c file to our project by right clicking on Standard Project Files and select Add Files To Project….

Adding a file to the project.

Once the main.c file has been added to the project, our IDE should look like this:

The IDE with the main.c file opened.

We can now build the project by either using either the Build option from the Build menu, or by hitting the Build icon. Building the project goes quite fast and soon we should see something like this in the Build window. For now the most important message is that we have 0 warnings and 0 errors.

Build output.

Now it’s time to get the binary code on our CPU. Assuming everything is properly powered up and the USB Smart Cable is connected we can go either to the Debug menu and select the Connect To Target option or simply click the Connect To Target icon:

Connecting to our Target/CPU.

If everything went as expected we should see this kind of message appearing in the output window:

Successfully connected to our Target/CPU.

Now it’s time to download the code. For this we go once again either to the Debug menu and select the Download Code option or hit the Download Code icon.

Downloading the code onto the target.

The download should start automatically but takes a bit of time to complete since the internal flash is erased first as well..

Downloading the code.

When the download is finished, we should see this kind of output:

The download finished successfully.

And now, we finally can run our little Hello World program. Go once again to either the Debug menu and select the Go option or simply just click on the Go icon:

Ready for a spin?

Assuming you have a TTL serial to USB converter connected to UART0 and a terminal program running on your PC, you should see something like this:

Using picocom as a terminal on my Linux PC.

The code we compiled will print this:

Using Uart 0
-----------------------
ZiLOG Developers Studio
-----------------------
i = 5
d = 25
f = 1.260000
eZ80F92 5 25 1.260000
-----------------------

After which the code simply waits for any user input which will then be echoed back. In this case Hello World!!!

Z20X talking with my real (as in hardware) terminal (see previous posts).
Close up of the output.

A few things to be aware of

Which crystal frequency to use

The Z20X BOM specifies a 18 MHz crystal. However, this is not a nice frequency when you want to use the Uarts. Simply because this frequency is not a nice multiple of the most common baud rates.

From the code we see that the value for the brg register (baud rate generator) is calculated like this:

    brg = SysClkFreq/(16 * UART_BPS);

With UART_BPS the actual baud rate we want to use. In the code it is set to 56,700 like so

    #define UART_BPS    57600

If we calculate the value for brg for a 56,700 baud rate:

18,000,000 / (16 * 56.700) = 19.841269...

This is not a nice integer value and the brg register can only accept an integer value. According to the C definition, the value in the brg register will be 19. This has an impact on the real baud rate of course. We can calculate the real baud rate as:

18,000,000 / (16 * 19) = 59,210

That’s roughly 2.7% off the desired baud rate. Depending how good your TTL serial to USB converter is, this might give reliability issues or not work at all.

These can easily be solved by using a 18,432 MHz crystal instead. For the same 57,600 baud we have now a nice integer value for the brg register:

18,432,000 / (16 * 57,600) = 20

For all the usual baud rates we will have now nice, integer values for the brg register and thus one source less of potential problems to worry about when things don’t work.

The reason why the BOM mentions a 18 MHz crystal instead of a 18.432 MHz one is because the clock cycle of a 18 MHz crystal is just a bit higher than 55 nano seconds.

1 / 18,000,000 = 55.5555... E-09

With a 18.432 MHz crystal, the clock cycle is just below 55 nano seconds:

1 / 18.432.000 = 54.25347... E-09

This is important since our external RAM is a 55 nano second part. Theoretically, it should fail for shorter clock cycles than 55 nano seconds. In practice however, those parts are typically faster than their specification claims and IMHO it’s no problem at all to use the slight higher 18.432 MHz crystal to be sure to have correct baud rates (and your code will run a little bit faster as well).

How to physically connect to UART0

The Z20X PCB does not have a UART connector (I’m told this might change in the future however).

The pictures below show my current solution. I simply soldered a few wires on the expansion connector of the Z20X board knowing that the following pins are used for UART0:

Port D, bit 0 is TxDO (pin 68 on the eZ80F92, pin 57 on the expansion connector)
Port D, bit 1 is RxD0 (pin 69 on the eZ80F92, pin 58 on the expansion connector)

Tapping the UART0 signals from the expansion connector.
Side view.
All wired up and ready to go.

Power usage considerations

The Z20X computer is powered via a USB cable. However it does take quite some power when the SSD1963 display is installed as well (I measured up to 0.8A peak). You definitely need a powered USB hub if you intend to power the Z20X computer via a USB port. A better solution is to use a dedicated USB charger (at least 5 Watt).

Do I really have to do all this clicking and typing?

You can find a ready made Hello World project here.

Enjoy playing with your new toy :-)

Recent posts

See more

Categories

About

Cocoacrumbs