A project to produce a kit for experimenting with z80 CPUs - More an embedded board than a PC
To make the experience fit your profile, pick a username and tell us what interests you.
I got a new job at the end of January so progress on this slowed a bit. I did manage to find some time this weekend to take a look at the EEPROM writes again.
Having completely forgotten my previous conclusions about writes being too slow, I spent quite a while digging into this. I got writes from the AVR itself to be even more reliable, but writes from USB still don’t work. Looking at it on a logic analyser, the write timing looks basically the same in both cases, except the USB case is about half the speed.
I just found the relevant bit in the data sheet: it’s not 10ms you have between bytes, it’s 150us - writes directly from the AVR are actually a bit over that but do work. Writes from USB are more like 500-700us so of course have no chance.
I might be able to buffer the writes a bit so they’re not directly in the USB handler, but then there’s still the chance that a USB request at the wrong time throws it off.
The options now are either to put up with it taking up to 10ms per byte to write (hence up to about 90 seconds to write an 8k eeprom) or to redesign it to be faster. It’s possible I might be able to find a way to buffer writes to between USB calls but that might or might not work.
When I chose the i2c port expanders, I knew it would be a bit slow, but I didn’t realise quite how tight the timing on the eeprom was.
Ah well, at least I understand the problem now. I’ll probably stick to the slow route for now and work on another part in the name of progress.
a couple of hours on a train has given me chance to play around a bit. It seems upping the i2c clock frequency helped when the writes were coming from the AVR itself, but not when coming from USB. For now I have a workaround where writes from USB are dealt with one byte at a time - it’s slow but at least it works.
I suspect the problem is that when writing to the EEPROM, after the first write you have 10ms to send another’s write before it commits the data to memory. I suspect a combination of the i2c timing and usb interrupts means that it’s missing that time slot. This would explain why the write protect wasn’t working, but also it probably explains why writes from USB only work one byte at a time. I’ll be able to confirm this and hopefully be more sure of what’s going on once I can check on a scope when I’m home.
The good thing about this is, while I will need to come back to it, it does at least work which allows me to work on the next bit of functionality, which is loading and saving hex files.
Once that’s done, I can work more on the z80 code, where my plan is to build up a set of libraries to make controlling the hardware on the board easier.
I had planned to do ore work on this by now, but I have managed to spend some time working through the next bits.
The USB interface is starting to come to life, and I can now reliably read back from the EEPROM and RAM. Writing to the EEPROM is proving to be a bit more challenging however - firstly, my write protect code seems not to be working, but it seems writing in general isn’t incredibly reliable. I suspect the timing is a bit off. Shouldn’t be too hard to fix but it might taking a bit of figuring out.
Once I get that figured out, I can actually start working on the Z80 side a bit more while carrying on to work on the programmer side to tidy it up.
It’s beginning to come together though!
Well, I finally got around to assembling a V2.0 board. They’ve been sitting around for probably too long heh.
The only major screwup I’ve found in the design so far is around the processor supervisor, a Microchip MCP100 I included to make sure the z80 CPU was correctly reset after power up. The problem was that when I pressed the reset button, it appeared to short the 5v rail to ground - upon further inspection, the MCP100 doesn’t just ground the reset line at startup then let it float on a pull-up resistor as I assumed, but actually forcefully asserts the reset line high - so the switch really was directly across 5v and GND! I decided to just remove the MCP100 since it’s not really needed - one of the things I included in this board revision is the ability for the AVR-based programming circuit to reset the z80, so I’ve included a bit of code in the firmware for that to assert a reset on startup to get things into a known state.
The USB circuitry seems to work well enough from the limited testing I’ve done so far, however the 1k5 pull-up resistor on D- should probably be on the main board supply rather than on the USB power pin. The idea originally was only power it when there’s something plugged into the USP port, however the actual effect it has had is to make it so you must power the board up before connecting the USB cable to it. The purpose of the 1k5 resistor is to tell the host that there is a USB device connected, and also indicate the speed of device it is. The problem is, upon sensing this resistor, the host attempts to talk to the device - but if the device is not powered on, then there is nothing to talk to, and the host assumes the device is malfunctioning.
Another relatively minor issue was that the ground planes make soldering the ground connections really difficult because they sink heat away. I probably should have used a larger iron tip, however, since I hope this to be available in kit form at some point, I should probably make an effort to make it easier to solder.
There will be at least one more hardware revision of the board addressing these issues and adding a few other nice to have features:
The next steps are software - I made some progress back in April with AVR code to drive the 16 bit i2c port expander, so I need to do something similar for the 8 bit expander used on the data bus, and also I need to finish off writing the host side of the new protocol, implementing some USB stuff on both sides, and porting the examples over to the new board. Oh, and documenting it!
There’s lots to do, but progress is being made!
I've spent most of this weekend laying out a new board design. It implements some of the improvements I mentioned before:
The biggest comment I have is... What with the port expanders, USB, and discrete logic IO. there are a lot of traces. As in, enough ttraces that I doubt I could fit any more on the board. There are a few things this design does not have that I said I'd add:
I think it's enough for now though, to progress the project.
Here's a preview:
Progress on the host library I talked about is... slow. It's quite tedious to write so I've been tending to play with the hardware more. Ah well, I'm sure I'll get to it.
the list of things to investigate is growing faster than I can dig through it!
Until now the interface to the AVR has been a text interface. This was great for initial debugging but it gets tedious to have to paste hex into a console, and isn’t great for machine control. I’ve designed a rough binary protocol for this, and I’ve started writing a library and GUI utility to implement it. The idea is the library can also be built into command line tools for uploads
Speaking of testing and debugging, the memory programming seems to be a little broken. Not sure what happened there but I need to figure it out at some point.
I stumbled upon z88dk and it looks like it might be quite useful as a way to make C programming for the platform easier, while still allowing calling precanned rom utilities.
Hmm many things to look into!
I've spent quite a bit of time so far this weekend trying to interface to the MCP23017 I hope to use to interface to the address bus. The ATMega328p documentation on the TWI (Two Wire Interface) are a little dense, yet missing some useful details.
Anyway I have something working on a breadboard now to prove the concept of controlling the external pins.
The thinking is I can use one of these and the similar MCP23008 to interface to the address and data busses, freeing up MCU pins and also allowing me to use a smaller, newer and cheaper AVR.
I think one of the next tasks is going to be to rework the comms protocol to make it less string-based. This is mostly to make it easier to control via software rather than via a serial terminal app. This would make it easier to upload code to the board than my current copying and pasting of hex files into a console!
I got the buttons working! I needed to add some pulldown resistors (not sure how I thought it would work without them!) but it’s all fine.
It got me thinking a bit though and while I was lying awake thinking about z80 instructions rather than sleeping, I had a bit of a thought explosion about this.
Note: This post is a bit of a ramble so feel free to skip ahead to the summary at the end if you get bored!
The more I think about it, the PIO isn't the best choice for interfacing on this project. It was originally chosen since it is the companion chip for the Z80 for IO in a parallel form, however it's relatively expensive and only provides 8 bits of input or output. It does have some fairly fancy features like interrupt support but to be honest, I'm not really sure how useful they'll be in practice on this project. It's also a fairly big 40 pin IC so isn't exactly great for board space efficiency.
The more I think about it, some discrete logic could do the same thing practically but significantly cheaper and probably easier to work a board design around. As a bonus, it would be easier to program for though it wouldn't support more advanced use cases
Expansion Ports and Usability
The first iteration of the design has a 50 pin IO port exposing various z80 bus pins (think address bus, data bus, various other interface pins and some spare CPLD lines lablled nominally as chip select lines)
This isn't a bad start, but I think I can do better. I was thinking about what it would take to port CP/M or one of the BASIC variants to the board, and then I realised I'd wandered into the early PC territory I said I wasn't targeting. It got me thinking about what would make this board more useful than just something to blink LEDs as a response to switch presses. At that point I realised it needs more IO.
I'm thinking as well as the LEDs it should have 8-16 inputs and outputs exposed on a header for simple external control. This would make it more like a z80-powered development board rather than something you talk to over serial or a keyboard.
That's not to preclude making it run BASIC or something, but it does give me some interesting ideas in terms of the hardware design.
Programming The Thing
Originally I'd envisaged a fairly bare metal programming style where the user's code runs straight away at boot and includes the hardware setup. This setup code could, of course be included from a library of snippets - I had thought about providing some files to include for this. The more I thought though, the more I think it makes sense to provide more than this.
One of the aims for this is to make the Z80 as easy to work with as possible and to demonstrate the different parts of a computer in a fairly simplistic form. It would be useful to provide a library of functions for the user to be able to call into, to save time and also to make it easier to get going. Of course they would be more than welcome to code from scratch if they wanted to, but the more I think about it, they shouldn't have to. I mean, they would still be coding in Z80 assembler, but at least they wouldn't have too much boilerplate to worry about.
Then it occurred to me that there's no need for the user's code to even include these libraries - by default the EEPROM could have the setup code on it that rarely changes and the user could just upload their own code to a known location. Of course there would need to be an easy way to upload the boot code as well in case they accidentally or intentionally overwrite it, but it would make it easier for the user to focus on their own code.
Then I started thinking even more, and it occurred to me there could be a whole library of code present on the board for the user to call into. In order to leave space on the EEPROM for user code, this could either be some snippets that get pulled in on demand or another idea I had was to include some flash memory that gets bank swapped in when the user wants to call into it. Flash...Read more »
After an Easter weekend spent mostly debugging circuits and sleeping, I finally have blinking LEDs - about three hour shifts after I probably should have gone to bed...
Anyway, the biggest problem I faced was the PIO not working. In the end, I used a CPLD dev board I had handy to make a simple latch, figuring it would be easier to debug the IO writes with that. After some playing around, I finally remembered that The high half of the address bus on IO reads/writes actually comes from other registers. I tweaked the address decoder to ignore the high bits on IO activity and it worked! Even better, when I went back to the PIO it was working too!
I’m not sure I have input sorted yet but it’s certainly progress.
I already have a pile of small improvements (and some not so small) that I would like to make to the circuit so I’ll be exploring those ideas soon.
the boards arrived today just in time for some Easter weekend fun!
I’ve assembled one and after some slightly tricky messes with the CPLD (firstly I forgot to configure the pin mappings when loading the completely untested VHDL file, secondly some pins got slightly bent on the socket so didn’t make contact) it generally seems to work as intended!
I haven’t tried actually programming the Z80 yet but the AVR is working as expected and the Z80 seems to be doing something in its unprogrammed state.
I’ve already decided the chip select lines need pull-ups - the issues with the cpld meant sometimes multiple chips were trying to drive the data bus which isn’t good (I first noticed when power consumption was slightly higher than expected!)
Next step I guess is to write some Z80 code! Or maybe procrastinate on that by working on the AVR uploaded more!
The only other issue I need to think about right now is that CPLD - it’s actually an end of life part, which means I’m having to use some programming software from 2013 and also of course it may be hard to get in future.
It’s not a huge issue for now but I will probably be looking for alternatives - there’s a Microchip part that might be suitable so I guess we’ll see. Part of me is tempted to make a partial address decoder out of discrete logic but that’s quite inflexible and would probably make this already relatively large circuit board even larger!
Become a member to follow this project and never miss any updates