The Haas at i3detroit has been acting up. Not the complicated functional part of the machine, just the monitor. It has a tendency to lose all vertical deflection (mostly) and flat-line. It’s still apparent that the signal into the monitor is ok, you can still make out that the screen that was being shown was flattened, but it’s like the vertical deflection part of the monitor just took a vacation. That vacation was not well deserved or earned from many long years of service. This monitor is from 2012 and has very little time on it. We got this machine donated in november of 2013 so most, if not all, of the time that monitor has had was in a light duty non-production capacity. So it has come time to fix the monitor.
Let’s step back in time to a happier place, VCF midwest 2019. A time when a packed convention hall full of people gawking over hardware ranging from slightly older than me to significantly older than me was perfectly normal and not a harbinger of disease and death. Ok, maybe not normal to everyone but at least not the other thing. I had come across some people selling unpopulated PCBs for various projects and I was happy to talk myself into some more stuff to do. The one I picked up that is relevant here is this MDA/CGA/EGA to VGA converter board. It takes digital video signals and converts them to analog video signals. This may not sound like an improvement, but VGA has been around for a LONG time and will probably still be supported for more time in the future. Those other standards… not much can take those signals these days. this converter board takes in the digital signals and builds a digital representation of the screen on the ram it has, then recreates a vga signal based on that image. The timings here are super critical and since they are not an even multiple of each other some clever work has to be done to make sure no data gets missed in either the saving of the data or the generation of the new signal.
I picked up this board to use it to get vga video out of the 80 column chip on a commodore 128 which outputs digital rgb video (or monochrome composite). I never got to try that. It is now sitting inside the haas at i3 translating from mda to vga and shooting the resulting image out to a generic little dell lcd monitor. This converter board is interesting because even though it takes a little soldering skill and patience to source parts and a programmer for the fpga onboard, it is still remarkably cheap for what it does. It is probably the cheapest MDA to VGA converter board out there right now. I will caution you when building this, the CoreEP4CE6 is cheaper than the chip onboard on its own. That should tell you the relative quality of this board is very, very poor. I had to reflow the solder on the qfp because so many were cold solder joints, but it does work. That has us sorted for now, but I still want to see about fixing this CRT.
In order to fix the CRT, I really need a way to drive it while it’s on the bench. I know some people are satisfied with function generators to make sync pulses and throw some junk in the video lines but I wanted something I knew was correct, something exact that I could calibrate to. I needed to generate a real MDA video signal. This is not as easy as you might think. MDA was an early monochrome computer video output standard, but even though it wasn’t particularly short-lived I don’t have anything that uses it. This means I need to synthesize it myself. At its heart MDA is a 16.257Mhz clock signal that drives everything. 720 pixels wide, each taking 1 clock cycle to display. Then 10 pixes of downtime, 135 pixels of a horizontal sync pulse (on a different wire), 17 pixels of downtime, then start again with another line of 720 displayable pixels. After 350 displayable lines the vertical sync wire goes low for a time the equivalent of 16 lines, then 4 lines of nothing and we’re back to displaying that first line again. That’s not so hard. Provided you have a source for that specific frequency clock and hardware that can make those pulses just right.
Doing this on a microprocessor is certainly possible, but you would need one faster than the signal you are trying to generate, potentially much faster if you are using a high level programming language or an architecture that takes many cycles to perform one instruction. You might get away with one slower than your generated signal if you use DMA but that is beyond the scope of this project. I decided it would take much less guesswork about timing if I were to just do it on an fpga. While processors (even extremely large and pipelined ones) are deterministic with respect to timing, it takes much much more work to determine exactly when something will happen and if it will continue to do so with any regularity. The fpga is set up to let me specify, every clock cycle, what happens. There’s a few things that made MDA generation different from VGA generation as you might find in books or other tutorials. First, let’s talk about signal levels.
My fpga development board of choice is the mojo v3. At the time it was one of the most fully featured and expandable boards out there and even includes an armega32u4 for uploading code and providing analog inputs. This is probably no longer the device of choice for hobbyists but it’s what I have. There were several add-on boards for my fpga available when it was new, one with ram onboard (because ram takes up a lot of space if you build it in an fpga, it’s cheaper to use a smaller fpga and have offboard ram), one with a bunch of microphones, one with hdmi ports and ram (potentially interesting, but for a later time) and one for connecting servos. I jumped at the servo shield because it boasted 24 pins of bidirectional 5v tolerance for my fpga pins. My intention for this bosrd was always to interface with 74xx and 40xx series logic so I figured the MAX3002 chips that were intended to talk to the PPM input on a servo motor would be just the ticket to talk to higher voltage vintage electronics. They are supposedly good to 20Mbps data rate which should be well over the 16.257Mhz we’ll be expecting them to do.
The short answer is that did not work, the level shifters introduced an unknown source of interference that made the video signals nearly unrecognizable. I lament that I don’t have pictures of the scope traces for this issue, but it was bad. Running the perfect mda signal into my known good converter gave me this:
The drive strength of the fpga pins themselves is enough to get in MDA range so I declared that good enough and was rewarded with this beautiful picture:
Now that we know how to wire the FPGA, what do we do about programming? (This is a bit backward from my bumbling, but it makes for a more linear explanation of issues and solutions). Fortunately for us, generating VGA video is a fairly common early project in fpga tutorials and we can cut out a bunch of stuff right away. First, we don’t need more than one bit for video (MDA does have an intensity pin, but it’s not populated on this monitor). That means any code talking about multiple bits per color can go right out the window. Next is an issue though, how do we get that clock signal we need? The pixel clock for VGA that the examples I found used was 25Mhz, it’s supposed to be 25.175 but 25 is close enough for most use cases and the monitor will just deal. To get 25Mhz from 50Mhz (the native clock of the mojo) you just divide it by two. That’s pretty easy, each rising edge of the main clock you flip the bit representing your slower clock, that means it takes twice as many clock cycles input to get clock cycles output. Divide by two. But how do you get 16.257Mhz from 50Mhz? You could divide by three and get 16.666Mhz which would probably work. For that you would want to act on both the rising and falling edges of the 50mhz signal, keeping your signal in sync and keeping the duty cycle 50%. That seems complicated (and the wrong frequency besides). How about something that will get us the exact frequency we want, but dithered a bit to account for not being divisible by the master clock?
Let’s imagine we are trying to manually line up our clock signals. After one tick of 50Mhz we pass 20 nanoseconds, great. How many do we want before we flip our 16.275mhz clock? 61.44ns. Ok, so keep going. After three cycles at 50Mhz we hit 60ns, still not where we want to be for our slower clock but the next tick will be a lot more. Do it anyway. At the next cycle we flip our slower clock, that’s 80ns we waited, equivalent to a 12.5Mhz clock, way too slow. We keep going. Our next chance comes at 100ns but for 2 cycles of our slow clock we want to be at 122.88ns. Next pulse is at 120, still not there yet. Next pulse is at 140ns and we switch. Now we have taken 2 cycles of our slow clock in 7 cycles of our fast one. That works out to 14.29Mhz on average. If we keep going we eventually average out to our 16.275Mhz clock, but our clock pulses are uneven. They may be uneven, but they don’t drift. Because we keep accumulating and reference the start time and not the last approximate pulse’s time it works out that we average our target frequency. This is like having a counter and adding a number to it over and over. When that counter overflows we still have the remainder (the ‘error’) and we continue counting to the next pulse fron where the first pulse should have been, not where it was. By keeping that remainder we make sure that our pulses do not drift from where they are supposed to be, but end up as close as possible to where they need to be. In my code that counter is 2^32 bits large (4,294,967,296 in decimal), and I add 1,396,465,667 to it each clock cycle. After three clock cycles it will not be full, but on the fourth it will overflow and have a remainder. I keep adding to that remainder until it overflows again, and again, each time incrementing the slower clock. Maybe I took more space explaining this concept than needed, but it’s the real difficult part of the project to get your head around. Everything else is just counting pixels or lines and deciding if you can draw on the screen or if it’s time for a sync pulse.
That’s really it. Based on the explanation above we draw for 720 slow clock cycles, then wait, then pulse another line for our horizontal sync. Do that 350 times and then we pulse the vertical sync line, wait, and start all over. I made some basic patterns to display based on that, of which the 10px border seemed the most useful. It turns on the video signal if it’s on the first 10 lines, the last 10 lines, or if it’s on the first or last 10 pixels of any line.
The code is a conglomeration of a bunch of examples and I’ve been unable to get back to it to clean it up into a nice module like the vga module it started as but for now it’s one big hacked up blob of verilog.
<previously the old code was pasted here, it is now uploaded to github>
The above code contains a bunch of cruft like using the built in button and LEDs, as well as a bunch of unused signals created by the video generation that would be good for drawing more complex images. Let’s see how it looks on the CRT.
Ok, who can see the pachyderm in the corner just staring at us? There’s something obviously different between the video the haas generates / CRT takes and the MDA signal my tester generates / the fpga converter likes. The Haas signal on my converter is shifted off the left side and my fpga signal on the CRT is shifted to the right. This tells me that the signal generated by the haas has a smaller hsync pulse than we’re expecting (or possibly smaller downtime). I’ve been told the hercules version of MDA (same signal, but bitmapped graphics instead of just text) uses a 16.000Mhz pixel clock, perhaps the hsync pulses are also smaller? I should go poke the haas with a scope and see how it actually transmits. It’s not perfect MDA.
EDIT:
I did eventually get around to creating a module for the MDA generation and moved the logic of deciding what to draw to the main module. This should allow better readability and the ability to do more complex things to the screen. That code is now hosted on github.