01/13/2019 at 16:31 •
After a house move, and office remodel, I'm finally able to look at this project again!
I did get a cheap Cyclone IV dev board (much more blockram), with onboard RGB565 VGA connector, PS/2 keyboard etc, but then found that it has NO spare GPIO to use for a serial port!
I also have the Lattice MachXO3 eval board (very cheap, seems nice), but the Lattice software just doesn't work for me on two different Windows systems. The JTAG programmer can't perform a scan.
Somewhere in the last year or so, I did make a small break-out board, to try out JLCPCB and EasyEDA, for a VGA resistor DAC, MAX3241 and PS/2 keyboard connector. I have yet to test one of those - psyching myself up for SOIC soldering.
On the VHDL side, I wanted to rework the VGA into a proper CRTC, like a 6845, with timing registers, and multiple clock dividers, to allow for various screen modes. The current VGA is all hardcoded for 800x600, which is not actually the right size for a true VGA text mode. Having a register file for the CRTC would allow the processor to change video modes too, much like a hardware system would. That would open up some potential weird retrocomputing possibilities, like Videotex, or maybe PLATO.
If I can get a working MachXO3, or back to the Cyclone II, the two immediate goals are serial from the MAX3241, and programmable video timings.
11/03/2015 at 14:30 •
I've got some standalone serial and VGA breakout boards now, so I decided to try moving over to the smaller EP2C5 board that was the intended target. This has a 2-bit resistor DAC per RGB channel for the VGA, and a MAX232 chip for the serial, so it can talk to the rest of the world. Except that it doesn't, even to my buspirate. So I (literally) dusted off my 1980s oscilloscope to see if I was even generating the right kind of thing...
Where it turned out I was out by a factor of 10 in my clock counting process, so running at more like 960 baud instead of 9600. With that fixed, I can see bits on the scope, but obviously not the right ones as the PC and Bus Pirate both still show nothing. The top trace is the 9600 baud clock, and the bottom one is the transmit line. I've just got a 10 bit buffer being rotated to send a start, ASCII 'A' and stop bit, currently. It might be time to get a modern DSO (and win back some bench space), because I can't really get this to trigger nicely to show a full word.
While I was fooling around with the scope, I also noticed that the resistors on my VGA DAC aren't very well chosen. The scope can't show an actual signal (it maxes out at 10MHz, I think, and the VGA dot clock is 50MHz), but I'm pretty sure these 4 lines should be evenly spaced:
10/10/2015 at 11:18 •
Things have slowed down a little with the terminal - partly due to the need to actually create something physical next. I've decided to learn how to etch my own boards, and make a little breakout PCB for the serial, VGA and PS/2 connections. Partly because I'll need them for the final target FPGA board, and partly because the dev board I do have seems to have something strange with the serial port. With my bus pirate, I can see traffic from the uart on the right pins, but somehow that doesn't actually appear on the db-9 connector at the edge of the board, and hence doesn't reach the PC. So I'll be using some GPIO pins and a breakout to make a serial port that I know for sure how it works. It looks like I will need an external 1.8432 MHz oscillator to get selectable baud rates, too - the onboard PLL can't generate that from the 50MHz system clock. You can get some serial clocks (e.g. 19200, 9600) OK, but I'd rather have the UART baud rate register work properly so it's all software-selectable!
On the HDL and software side, there's a makefile to build the z80 firmware and the FPGA bitstream now. I finally fixed the annoying whistling noise from the dev board, and the Z80 does some more self-test now. I really need the I/O to get further - keyboard in, and serial i/o - so I've got a cheap UV exposure box ($15 from ebay, intended for curing nail varnish), a little chemical kit, and the free version of diptrace. Time to ruin some 2-inch squares of FR4...
09/21/2015 at 22:25 •
I wrote some quick assembler loops to blast the display RAM with characters, and it was strangely lumpy. I eventually figured out that the counter that I was pushing onto the stack to make room for another counter (one for rows, one for columns - the handy Z80 loop instruction (DJNZ) only works on a 8-bit register) was not actually being saved anywhere. So the lumpiness between bursts of update was while we waited for the Z80 to go all through the rest of the memory map trying to write before coming back to display RAM.
With that fixed (same error as the display RAM decoding, d'oh), the stack works, and the screen updates are really fast. I also got my dev board's LEDs hooked up to a Z80 I/O address so that software can signal status.
Finally, I started on a build script. It will turn into a makefile, but for now a batch file will assemble the Z80 ROM, pad it to 4K, convert it to intel-hex and tell Quartus to rebuild the FPGA image without synthesis. That all takes about 4 seconds instead of much longer for VHDL hardware changes.
I've also been looking around for a nice small laptop that's old enough to be worthless to normal punters, so I can get a cheap one on ebay. It needs to have a known LCD screen and either a documented or brain-dead keyboard ideally. I like Lenovo keyboards, so perhaps an old Thinkpad X? Failing that, my beloved 12" Powerbook G4 might be a candidate.
Back to the serial port now...
09/19/2015 at 17:29 •
I fired up the project in ModelSim, and added all the Z80 and memory-related signals to it. From there I could see that memory requests in general were working OK, and could watch the Z80 pull bytes from ROM in low memory. I could also see where we got to the "LD (HL),A" instruction, that should update the display RAM at F000. F000 turns up on the address bus along with MREQ' and WR' and 65 on the data bus (a letter 'A' in ASCII). However, the chip select lines for the display RAM stay at 1 (off, it's active-low) and the request goes to the ROM instead.
Here's a picture of the problem. I think this stuff is really cool! Because your design is 'soft', you get a infinite-channel logic analyser for free... the DUT/A line is the system address bus, and DUT/DISPRAMCS_n is the display RAM chip select, with the others above it. At the yellow cursor, the Z80 has just asked for F000 - the actual write comes a clock later (clock at the top, WR_n a few lines below). Neat side note: You can also see the built-in Z80 DRAM refresh mechanism happening, which is why the address bus keeps jumping backwards and forwards - it's two sequences. One is driven by the Program Counter, and is fetching instructions, or data from memory with MREQ low, and the other is cycling through a 7 bit counter with RFSH low, to help you keep your DRAM refreshed. It does this while the rest of the processor is thinking about the current instruction, so the bus isn't required for memory or I/O access.
Anyway, knowing that the issue was with decoding, I noticed that I was comparing a 5-bit value to a 4-bit value for the display ram address decoding - something that the VHDL complier normally complains about - corrected that, and we were up and running!
For about 20 seconds.
Because now the address decoding is working, none of the RAM is optimised away, and the compiler has noticed that I still have dual-port, dual-clock RAM. Apparently, the same-size data buses isn't enough to fix it. So I lied - I told the system that I had the rev B fixed silicon. I actually have no idea if I do, because I can't figure it out from the label on the chip, but the symptoms are "possible memory corruption in certain circumstances", and since I have (mostly) one writer and one reader, I'm hopeful that I'll be OK. There is another workaround, but it uses twice as much blockram, and I don't have that to spare.
So the final exciting result:
"AH LOOK" used to be "OH LOOK", and the "A" was put there by the Z80 software! It's not astonishing, but everything is talking now. Also, you can see the column-9 extension stuff working in the box graphics below it, to produce continuous horizontal boxes.
On to some slightly more advanced software to test i/o via the UART, a bit more virtual hardware to generate the 1.8432 MHz clock that the UART wants to generate baud rates, and some real physical hardware (MAX232 for now since I have one handy) to get the serial port to the right voltages to talk to other devices.
09/18/2015 at 22:21 •
VGA side now works with 8-bit fetches from display RAM, and the design synthesises again! Also, fixed a timing issue with the Flash attribute where it flashed a character early, and added in the logic for extending box-graphics characters so they join up.
I also finally slowed down the Z80 clock enough that I could see A11 toggling away. Yay! Then I found that I can edit the contents of FPGA blockRAM (including ROMs, but not dual-port RAM) while the system is running, using the Quartus JTAG tools, so I was able to prove to myself that it's really fetching instructions from the ROM by moving the JP 0000 nearer and further from the end - A11 flashes faster, and not at all if you put the JP instruction in the lower 2K of ROM. So that's cool. I found the option to rebuild the bitstream with new memory images without changing the logic too (3 seconds vs 2 minutes), which will be helpful for the software side of things.
However, the 9 bytes of real code in the ROM was supposed to write a character to the display RAM, and that isn't happening. In fact, the synthesis tools seem to be optimising the 1K (other) RAM away altogether, so something isn't quite right there. I suspect it's with the address decoding logic (a funny mix of active-low Z80 signals and active-high signals for the IP memory), so it might be time to get the simulator going again, although with a full CPU in there it might take a while to run!
- Writeable RAM
- Figure out an OK development environment for writing Z80 assembly and/or C.
- Get the UART hooked up to the outside world and prove that it reads/writes, too.
- PS/2 interface is next, I guess.
09/14/2015 at 22:06 •
I patched in the T80 processor, a 4KB ROM (empty apart from a JP 0 at the end), a 1KB SRAM and a 16450 UART today. It took surprisingly little fighting to get it to synthesise, copying mostly from the "DebugSystem" toplevel supplied with the T80 tarball, although when I started to connect up the 'A' port of my dual-port display RAM, it turns out that the Cyclone II FPGA I'm using doesn't actually support dual-clock, dual-port blockram, even though the Altera software initially says it does! [*] I also found yesterday that I can't create PLL components, even though the chip does have two clock managers, which is a problem for the pixel clock change.
New problems then:
- Find a way to prove the Z80 is actually running. LEDs on the A11/A10 lines don't appear to be cycling, with a roughly 1.5MHz CPU clock. Maybe I need an even slower clock. Once I can see the bus moving, I'll add the row of blinkenlights on the FPGA board as an I/O port.
- Figure out how to get data from the Z80 into display memory. Could this be using more I/O ports and some registers to track the cursor and write data? The screen doesn't *have* to be memory mapped...
- Work out the counters to centre and mask the 80x25 display in the 800x600 screen, since I'm apparently stuck with that.
Good news is that everything (CPU, UART, VGA, misc) fits into about 2700 LEs, assuming that Quartus isn't optimising anything away. Also, the flash attribute works as expected.
Testing the UART will require a working CPU, and also the external interfacing that I've been putting off wiring up. That will also get me 16 colours though, and a PS/2 port.
[*] Specifically: dual-clock, dual-port with different sized ports. I use 8-bit on the z80-facing port A, and 16-bit on the VGA port B, to fetch the character and attribute bytes in one shot. There is probably time to do two 8-bit fetches there.
09/13/2015 at 21:01 •
Data is fetched from display RAM now, and the attribute byte is decoded correctly. I used an ANSI-art editor in DOSBox to produce a test screen and saved it as a .bin file, which is a VGA memory dump. The FPGA tools don't really distinguish internal between ram and rom, so it's perfectly OK to have RAM but preloaded with data at startup. The display RAM is full-dual port, dual-clock, so the Z80 can live in its slow 4MHz 8-bit world while the VGA side of things grabs 16-bit words at a time. For updates from a serial port, I'm not too concerned about tearing etc. The screen is showing fg and bg colour changes, and a full-ish ASCII character set. Attribute decoding worked first time, pretty much.
There's no bounds-checking on the display though, so outside of the 80x25 main area, there is random overflow/wrapped data. I should be suppressing before the first real character, on the left, and after column 80 (81). A real VGA textmode runs in 720x400, not 800x600, so I have a bunch of extra space on the screen. Unfortunately, without switching to using external RAM, I can't really use it to display anything, so it'll most likely be blank, or I'll figure out how the Dynamic Clock Manager works to generate the right 35.5MHz dot clock for 720x400@85.
You can also see where I haven't yet implemented the column-9 extension stuff for box graphics.
Still haven't figured out the whistling noise. Tying the relevant I/O pin to a constant-0 output has not helped.
- column-9 extension
- test flashing
- bounds-checking and/or resolution change
09/11/2015 at 10:12 •
There's a github repo with my VHDL code in it (and eventually the Z80 assembly too): howardjones/fpga-vt. It's my first attempt at an HDL implementation (or any significant-sized digital logic actually, I'm a software person), so please be gentle.
Today's software vs hardware lesson: I already knew this at the back of my head from writing 68k assembly years ago, but I noticed that my Logic Element usage on the chip jumped up from 200 to 1600 some time recently. Looking at the RTL, I found a massive array of logic gates eventually feeding into the 'flash' flag (which is ANDed with the actual pixel to decide if the foreground or background is shown on a flashing character). Why? because I have a counter to count frames ("jiffies") and used that to decide if the cursor or character flashing flags need to toggle. I wanted them to flash at different rates so that the cursor wouldn't get lost. My rates were "mod 27" and "mod 36". Switching to 24 and 32 saved 1400 LEs, or about a quarter of the entire FPGA (roughly 5000 LEs on the target board, 8000 on my dev board).
09/10/2015 at 21:14 •
Actual characters on an actual screen. What I thought I had worked out ages ago was not really correct.
This is 800x600 @ 72Hz SVGA mode, with 9x16 character cells and 8x16 font. The red bars are 8 pixels wide which is why they drift out of step with the characters. On a VGA screen, line-drawing characters are extended into column 9, to allow you to draw continuous boxes, so column 9 of the character is generated from column 8, if the character is within particular ranges, otherwise you get the spacing between characters.
The actual character grid will start in further from the edge of the screen to allow time for the first character to spool up (fetch from screen ram, fetch from font rom, and interpret the attribute byte).
- Wire up the 2-bit R/2R VGA DAC and a new DB15 connector - currently the dev board's built in VGA port uses only one pin each for R,G,B and I need 16 colours, not 8. (Final target is a smaller board with no fancy I/O anyway)
- implement attribute decoding, and a latching register for the fg/bg colours to latch on the new character (after being prepared during the previous one).
- implement the selector logic for fg vs bg with flashing (flashing is already there).
- implement screen RAM with altsyncram - I already have some ANSI gfx in screen RAM format to test with.
At that point, we should be ready to hook up a CPU!
Also, on the dev board I'm currently using, the buzzer produces a high-pitched whistle, despite nothing being assigned to its I/O pin. It'd be nice to not have that. I also just noticed that C, L and U are missing from the character set below... probably not a coincidence that there's 8 characters in between each missing one.