- 1 Computer Design Principles and Programing
- 2 Introduction to Z80SoC
- 3 Z8SoC Architecture
- 4 Preparing the FPGA Development Environment
- 5 Coding the BIOS and Programs
- 5.1 Coding Tools: The Assembly Compiler
- 5.2 Coding Tools: The C Compiler
- 5.3 Coding in Assembly
- 5.4 Coding in C
- 5.5 Updating the ROM
- 6 Z80SoC Hardware Description in VHDL
- 7 Conclusion
Computer Design Principles and Programing
Note: This is a hobbyist project, which takes a very simple and practical approach to the principles of computer designing and implementation. It also uses a short cut to implement the computer: FPGA technology.
Have you ever imagined how complex is a computer, and how the various internal and external components interact with each other to ultimately be considered a helpful tool, something that helps us in our daily tasks or providing us with some fun playing games for example? Learning the basic principles of engineering for computers that were used in the 70s and 80s to create our now beloved 8-bit machines is not that complicated , and this is the challenge that I propose to all those curious souls who wants to understand how to make a computer, from the architectural decisions such as RAM size and resolution video to a proper memory mapping layout and peripheral control using ports or memory addresses, through the BIOS design and its programming in assembly language and C, to the development of a complete software such as a game that explore the resources implemented in the hardware. Interested? So let's lay down the objectives for this series of articles , and even more importantly, what is not part of the objectives.
- An educational project that aims to study the anatomy of a computer, understand its operation and how the different parts interact;
- How to design a computer in every detail, from the architecture, specification of its resources, implementation using VHDL, BIOS development;
- Use of FPGA Developments Kits, use of VHDL as the hardware description language to implement the computer in the FPGA , and use of C and Assembly languages to develop the BIOS and software;
- Learn how ot use the most popular FPGA development kits FPGA by the community: Terasic DE1, DE2 Terasic, Xilinx Spartan 3E.
Not covered :
- This project is not a step by step guide on how to make a MSX, Spectrum, TRS- 80, or (your favorite computer here). If that is what you want, please refer to the references section, or run a internet search. There are plenty of projects ready to use and to run games if that is your purpose;
- We will not be designing printed circuit boards, soldering wires ro components. All components we need will be implemented using VHDL and upload to a FPGA development kit;
- We will not create a general purpose computer to use at home - the result of this project will be an 8-bit system for experimentation and learning. You will be able to write software or games for it, tough.
It does no matter if you can not code in assembly or C. By the end of this course, you will have acquired some basic knowledge and maybe actually learn some Z80 assembly and C.
Also it does not matter if you can not specify hardware using VHDL. At the end of this course you will have learned the basics of VHDL and should be able to read other projects and have some understanding of it, and even start doing modifications of your own in the Z80SoC.
And of course, if you don't understand how the software interacts with the hardware, how programs drive components connected to the computer, then be very welcome because this is one of the most interesting aspects of this course, since in this course it will be described and show how hardware and software interact.
Ready to Start?
Please refer to the table of contents at the top of the page to quick jump into topics, and let's start it!
Introduction to Z80SoC
The Z80SoC arose from a sudden interest in FPGA things, sometime in 2007. I do not remember exactly I became aware of FPGAs, but the wide range of applications won me over instantly and I literally stopped everything I was doing (excepting eating and working for a living) to dedicate myself to get more knowledge about this technology, its possibilities and how to use it. To fully explore the capabilities of FPGAs, it is required to know a language to describe the hardware you want to implement and so I got into VHDL as well.. Designing for FPGAs can be done in VHDL, Verilog and other languages, but after some research and experimentation with these two languages I chose VHDL, and that is the description language that we will use in these articles.
Z80SoC rodando ROM de demonstração
Next step was to identify the best development kits to explore all capabilities of FPGAs.
FPGA Development Kits
After some research on forums, and another extensive internet search for existing projects of retro computers and consoles for FPGA development kits, I came to a conclusion that the vast majority of projects used mainly two developments kits readily available:
- Terasic DE1, winh an Altera FPGA;
- Xilinx Spartan 3E, with a Xilinx FPGA.
With these two kits it will be possible to implement and play with a number of different vintage computers and game consoles such as:
- Atari console
- TRS-80, TRS-80 Color Computer 1, 2 e 3
- Commodore 64
- PC 8086
- ZX Spectrum
- Arcade games (Pac Man, Galaga, Defender, Space Invaders)
- … many others!
Following the research, my choice and therefore my recommendation to buy Is (by cost benefit order):
If you can buy a single kit, get (1). If you can afford two kits, get (1) e (2).
If you have acquired already a good knowledge of VHDL to the point of being able to adapt and modify existing designs, you may consider buying kit (3) or (4). These two kits should not be the first choice for a beginner reason being they much more capacity internally and therefore are much more expensive than the DE1 board. And being more expensive, means less people got them, reason why it will be much less common to find projects ready to use for these two kits, and consequently they will be underutilized if you do not have ability to adapt projects developed for other kits .
The Z80SoC is a system allows text mode 80 x 40 (80 columns and 40 lines of text), and pseudo graphics since the character table may be rewritten after the boot (the bits that compose each character is copied from ROM for CHARRAM, which is a RAM and therefore can be written). You can be write to video using memory addresses or I/O ports. It is possible to write on the LCD display in the Spartan 3E and Altera DE2 in the same we write into the Video Memory or RAM, and the characters will be displayed instantaneously. The Spartan 3E implementation also allows to read the state of the knob, and in the Terasic DE1 and DE2 it is possible to write to the 7-segment displays. All these operations are performed writing into memory addresses or I/O ports as previously mentioned, and via programs developed in assembly or C. I was developed a basic library of functions using SDCC to program in C for Z80SoC, allowing to drive and read signals from the computer peripherals, such as LEDs, LCD, memory, keyboard, etc. The system was create using “parts” from the opensource community. The opensource community can be seen as a parts provider for such systems, since we can search, find and use those freely available components to build systems such as the Z80SoC. These “parts” are known as “core”, and there are cores available for the various components of a computer, such as ram controller, serial controller, VGA controller and so on. The cores used in the Z80SoC are listed in the following table.
|CPU Z80||T80 from opencores.org|
|VGA Controller||From book “Rapid Prototyping of Digital Systems“|
|Keyboard controller||Adapted from unknown source|
|LCD Controller||Adapter from core from Rahul Vora, New Mexico University|
|7SEg Display controller||Developed for Z80SoC|
|Rotary button||Adapted from Xilinx Spartan-3E reference core|
|LED, Switches and Push Button controllers||Developed for Z80SoC|
|SRAM controller||Developed for Z80SoC|
|CHARRAM memory controller||Developed for Z80SoC|
|ROM memory controller||Developed for Z80SoC|
|BIOS and software||Developed for Z80SoC|
Nota: Architecture diagram will be inserted here briefly.
Avoid Trouble: From now on, we may refer to the different FPGA kits as “platform”.
Before start building a computer, we need to define what resources we need on it. This is a very simplistic definition of the computer architecture or computer design and specification. In this initial phase, we must take into consideration things we need or things we would like to have on the computer, such as:
- what microprocessor to use
- main memory and its layout
- text-mode resolution
- graphics resolution
- number of colors
- sound, number of channels available
- external peripherals (keyboard, mouse, monitor output, disk system, K7)
Once defined the required resources, we must consider the viability of our design. For the Z80SoC we have to think in terms of FPGA capacity, and component development in VHDL. Note that if we can't find a core that fulfill our needs, than we need to develop the component ourselves. The steps to define the specification of the Z80SoC for each development kit – the architectural decisions - will bi discussed within the section dedicated to each component. But its worth to mention now that the kits we will be working with has different specifications, directly driving the capabilities or limitations of our computer. Nevertherless and despite the development kit you might have, it will be a fun and challenging journey.
As an overview of the difference between the FPGA kits and the resulting impact in our computer, we have for example a difference in RAM size for DE1/DE2 (40KB) an the Spartan-3E (only 12KB). This happens because even though the three kits has SDRAM available, we will not be using it due to the complexity to implement a controller. We are left with the possibility to use the Terasic DE1/DE2 SRAM, or the Spartan-3E BlockRAM (since the S3E don't have SRAM). But since BlockRAM is a very limited resource, the S3E can't have the same 40KB of RAM as the DE1/DE2 kits. This was only an example of the restrictions and limitations we must overcome to accomplish a satisfactory design. Other challenges will appear during the course and we will discuss in details each case.
+-------------------+ 0000h | | | ROM | | 16 KB | | | 3FFFh +-------------------+ 4000h | VIDEO RAM | | 4800 Bytes | 52BFh +-------------------+ 52C0h | System | | Variables | 57FFh +-------------------+ 5800h | | | table | | (charram) | 5FFFh +-------------------+ 6000h | | | RAM | | | | 40 KB | | | | | +-------------------+ FFFFh
+-------------------+ 0000h | | | ROM | | 16 KB | | | 3FFFh +-------------------+ 4000h | VIDEO RAM | | 4800 Bytes | 52BFh +-------------------+ 52C0h | System | | Variables | 57DFh +-------------------+ 57E0h | LCD VIDEO RAM | | 2 X 16 | 57FFh +-------------------+ 5800h | Characters | | table | | (charram) | 5FFFh +-------------------+ 6000h | | | RAM | | | | 40 KB | | | | | +-------------------+ FFFFh
+-------------------+ 0000h | | | ROM | | 16 KB | | | 3FFFh +-------------------+ 4000h | VIDEO RAM | | 4800 Bytes | 52BFh +-------------------+ 52C0h | System | | Variables | 57DFh +-------------------+ 57E0h | LCD VIDEO RAM | | 2 X 16 | 57FFh +-------------------+ 5800h | Characteres | | table | | (charram) | 5FFFh +-------------------+ 6000h | | | RAM | | | | 12 KB | | | | | +-------------------+ 8FFFh
External peripheral control will be implemented using the micro processor I/O ports. Whenever possible, the I/O ports will have the same address and function across all platforms. Some platforms will implement more or less I/O ports following the availability or absence of resources.
|01H||Out||Green Leds (7-0)||DE1 / DE2 / S3E|
|02H||Out||Red Leds (7-0)||DE1 / DE2|
|03H||Out||Red Leds (15-8)||DE2|
|10H||Out||HEX0 to HEX1||DE1 / DE2|
|11H||Out||HEX2 to HEX3||DE1 / DE2|
|12H||Out||HEX4 to HEX5||DE2|
|13H||Out||HEX6 to HEX7||DE2|
|15H||Out||LCD On/off||DE2 / S3E|
|20H||In||SW(7-0)||DE1 / DE2 / S3E*|
|30H||In||KEY(3-0)||DE1 / DE2 / S3E|
|80H||In||PS/2 Keyboard||DE1 / DE2 / S3E|
|90H||Out||Video||DE1 / DE2 / S3E|
|91H||In/Out||Video cursor X||DE1 / DE2 / S3E|
|92H||In/Out||Video cursor Y||DE1 / DE2 / S3E|
(*) 4 Switches only in the Spartan-3E.
System Variables and registers are internal attributes initialized at boot time with standard default values. These variables and registers have specific purposes such as to identify the platform, store the memory address where RAM starts and ends, and so on. The registers can be of type read-only or read-write.
|57DFH||R||Platform: 0=DE1 / 1=S3E / 2=DE2|
|57DEH||R||Last key typed|
|57DCH||R||LCD VRAM Start Address|
|57DAH||R||RAMTOP (Last available RAM address)|
|57D8H||R||RAM Botton (First available RAM address)|
|57D6H||R||CharRAM (Initial address)|
|57D4H||R||VRAM (Initial address)|
|57D2H||R||Stack address (Z80 stack, initially the same as RAMTOP)|
|57D0H||R/W||VRAM address where next char will be written|
|57CFH||R/W||Video coordinate X|
|57CEH||R/W||Video coordinate Y|
|57CDH||R/W||Standard output peripheral|
|57CCH||R||Text Horizontal resolution (number of columns)|
|57CBH||R||Text Vertical resolution (number of lines)|
Preparing the FPGA Development Environment
We will be using Xilinx ISE and Altera Qaurtus II Web Edition, both free software.
Download the Software
You should download only the software for the platform you will be using, unless you have got both Altera and Xilinx development boards, i that case download both softwares below.
Avoid trouble: Stick to the versions indicated above. Using a version other than the suggested will require you to adapt or perform changes in the design or de componentes generated using the Core generators. If you are inexperienced user, you will be facing unnecessary problems.
Create the Z80SoC Project
- Create a directory for the project, for example Z80SoCv0.7.3
- Unzip to a directory named Z80SoCv0.7.3 such as that you will end up with this structure:
Z80SoCv0.7.3/ Z80SoCv0.7.3/memoryCores/ Z80SoCv0.7.3/vhdl/ Z80SoCv0.7.3/ROMdata/
Steps to Build the Project and Load the Design
- Run the Design Software (ISE / Quartus II) and open the Z80SoC project.
- Build the project. The build process will generate the binaries files:
- Altera: a .sof binary file.
- Xilinx: a .bit binary file.
- Using the FPGA Programmer, upload the binary file to the FPGA..
Changes in the Design or Components
Some componentes were created using a generator plugin available for both Altera and Xilinx. They should be used every time changes are required to those cores.
Whenever changes are made to the BIOS, the ROM file (ihx or mif format) must be copied to the ROMdata directory, the project should be build and upload to the FPGA.
Coding the BIOS and Programs
This section will cover the tools required to develop for the system. To develope a BIOS to a computer, it is necessary to have a fully understanding of of hardware specification, memory map, I/O ports, and peripherals available and hot they should be programmed.
The concept around a BIOS is that it should have all the low-level routines to access those peripherals, driving the hardware directly and hiding this complexity from more high lever user applications. Once a BIOS is developed, it can be used by other programs, which in turn can be programmed without having knowledge of how the hardware should be driven.
Avoid Trouble: Start using the tools listed below, unless you are experienced and can easily adapt the instructions for other tools.
Coding Tools: The Assembly Compiler
To develop in assembly, we are going to use pasmo, a multi-platform cross-assembler. Pasmo allows you to compile your Z80 programs on Windows, Linux or Mac OS X. You may be require to compile from the sources, tough.
Coding Tools: The C Compiler
SDCC stands for Small Device C Compiler, as is designed to generate small and optimised code for micro controllers and 8 bit processors, such as the Z80. It also provide some extensions to easily access I/O ports, extensively used in the Z80SoC.
SDCC is here: http://sdcc.sourceforge.net.
Coding in Assembly
Now the fun begins.
We haven't created the hardware yet, but it is completelly possible to start developing for it since we already have a detailed specification of its interfaces. I refer to #Z80SoC_Features, #Memory_ Mapping, and #I/O_Ports.
With that information in hand, we are able to start developing for our computer.
In this section we will describe the assembly code to drive the various peripherals available in the computer. Lets start with the simplest, and go forward to the more complexes.
Each platform have at least 8 green LEDs (DE2 will have 9 green LEDs, but the ninth led will not be available to the developer).
Each LED will need a bit of data to set it ON (1) or OFF (0). That is, set the bit to 1 and the LED will light up, set the bit to 0 and the LED will go dark. To set each led ON, we must use I/O Ports as listed in I/O_Ports. According to that table, the green LEDs (7-0) are driven by port 0x01. By (7-0), it means there are 8 LEDs, numbered from 7 (left-most LED) to 0 (right-most LED).
To drive the eight green LEDS, we need 8 bits (or a byte). Each bit will correspond to a LED.
These 8 bits of data should then be send to port 0x01.
Some examples: To switch ON all 8 LEDS, we should send b11111111 in binary (0xff in hexadecimal) to port 0x01. To switch only the most right LED (LED 0), we should send b00000001 to port 0x01.
To drive a LED, we use the Z80 instructions OUT (port),value. Therefore, as part of out Z80 library to access the Z80SoC green LEDS, we might have this sub-routine:
LEDG: out (0x01),a ret
Whenever we want to switch a green LED state, we set the A register with the correct value and call LEDG. Of course, you can simply use the OUT instruction directly without calling the sub-routine (it will even be more efficient and produce a smaller code), but we are defining the sub-routines here (and later, functions in C) to illustrate the concept.
Also, you noticed that a single OUT will change the state of all 8 green LEDs. It is up to the developer to save the last LEDs state in memory, and replace only the bit corresponding to the LED he wants to change.
Red LEDs are available only for DE1 and DE2, and are driven through port 0x02. Referring to (#I/O_PORTS, you also noticed that the DE2 have an extra set of red LEDS (18 in total, but only first 16 is available to the user). The second set of of LEDs on the DE2 (15 to 8) are driven by port 0x03.
The sub-routine to drive RED leds in our library should look like this:
LEDR07: out (0x02),a ret LEDR815: out (0x03),a ret
7 Segment LED Display
The seven-segment LED display is driven per pair of segments. That means, each two "letter" or "number" are driven by one port, starting on the right and moving to the left. The DE1 have 4 segments, and the DE2 have 8 segments. S3E don't have seven-segment LED displays, so this sections does not apply to it.
On the DE1 and DE2, to show "ABCD" on the display, sent two OUT commands to ports 0x10 and 0x11 as follows:
ld a,0xCD out (0x10),a ld a,0xAB out (0x11),a
For the DE2, there is an extra set of segments, addressed by ports 0x12 and 0x13.
The sub-routines in the BIOS should be like this:
Hex0: out (0x10),a ret Hex1: out (0x11),a ret Hex2: out (0x12),a ret Hex3: out (0x13),a ret
Some times, we need to drive all four segments at once (for example, to show a memory address. To facilitate this task, we can add the following two sub-routines to our BIOS:
HEXDATA: ;; hl = data to print on HEX display push bc ld c,0x10 out (c),l inc c out (c),h pop bc ret HEXADDR: ;; hl = Address to print on HEX display push bc ld c,0x12 out (c),l inc c out (c),h pop bc ret
Avoid Trouble: Note that the sub-routines HEXDATA and HEXADDR saves the register BC to the stack before changing it. This is a good programming practice, that will save lots of troubleshooting later on when your program starts behaving unpredictably due to your registers being overwritten by sub-routines.
All platforms have available four push buttons. The Spartan-3E have an extra push button: the Rotary button. In out project, the rotary button on the S3E will be 5th button, and all of them will be accessible by port 0x30.
To detect if a push button is pressed, read port 0x30. The bits will indicate if the button is pushed (bit equals 1) or not (bit equals zero). Bits should be read from right to left, and 5th bit will represent the state of the rotary push button.
PUSHBUTTON: in a,(0x30) ret
The LCD on the DE2 and S3E consist of a display with 2 lines of 16 characters. The characters are memory mapped on addresses starting at 0x57E0 and finishing in 0x57FF (consult #Memory_Mapping. To write into the LCD display, all you have to do is use the Z80 LD instruction to write the ASCII codes to these addresses.
ld a,0x41 ;Load "A" into register A ld (0x57E0),a ;show the character on LCD, first column of first line
The following BIOS sub-routine will be implemented to help displaying full messages into the LCD easily.
LCD_print: ; hl = address of a 32 characters string to print push de push bc ; get the LCD start address ld de,(0x57DC) ld bc,32 ldir pop bc pop de ret
An example of use to this routine is:
ld hl,bootmsg call LCD_print bootmsg: ; message must have 32 characters length db "Booting... " db " "
Rotary button is only available on the Spartan-3E platform. It is a nice addition, tough and can certainly provide an extra fun factor to some kind of software such as games.
This is a more complex component, and its controller was implemented using a reference model from Xilinx, and integrated into the computer design as a peripheral. The interface to this component is very simple, tough, and basically consist of reading port 0x70 and evaluating the value, which can be one of:
- b00 (or zero in decimal): Button not rotating.
- b01 (or 1 in decimal): Button rotating to the right.
- b10 (or 2 in decimal): Button rotating to the left.
The routine to read the rotary button is shown below. But remember that you can read the port directly using the Z80 instruction IN instead of calling this sub-routine.
READ_ROTARY: in a,(0x70) ret