BenEater inspired video card

 Matt's Ben Eater Inspired Video Card





I absolutely loved Ben Eater's "World's worst video card" video series (https://eater.net/vga) and wanted to use what I learned in the videos to make my own; a simple circuit to create a VGA signal and display an image on a screen.

My Background / Disclaimer

I'm a complete amateur. I do not work with electronics for a living. I'm a programmer professionally, but one day I realised I didn't know enough about how computers actually worked, so I only tinker with this sort of thing as a hobby and a way of learning more. I have no idea if what I've done is smart, or even completely safe, so please don't take anything I write as advice coming from a place of experience, because it isn't.

Preparation

If you don't care about how I came to take on this project and want to get right to the technical stuff, feel free to skip the this section of this write up.

About 6 years ago, my wife bought me a raspberry pi and a SunFounder intro to electronics kit for Christmas. I really enjoyed playing with it, and quickly built up a collection of electronic components, ICs, and a few tools. Soon after I read a book to learn more about how computers work, called "But How Do It Know?" by J Clark Scott. It is a fantastic book and I highly recommend it.

My plan was to build a computer from scratch. I made a few parts of it on breadboards, but then my son was born, and I struggled to find time between work and nappy changes to work on any hobby projects. So it was all left abandoned.

At some point over the years, I discovered Ben Eater's YouTube videos, and watched them all - probably twice! I really wanted (and still do want) to finish building a simple 8 bit computer, but at this stage in my life I just don't have the bandwidth to commit to something quite so time consuming.

Then "The world's worst video card" series came out. It's relatively small in scope, and the reward of being able to display (terrible quality) images on screen really appealed to me. Due to the 2020 pandemic, I was stuck indoors anyway, so I decided to treat myself to a hobby project, and this would be it.

After commandeering part of the desk in my (now 5 year old) son's makeshift home classroom, I purchased the least expensive oscilloscope I could find, and a signal generator. The phrase "all the gear, no idea" springs to mind.


My goal

I set myself the following goals for the project:
  • Create a vga signal using Ben Eater's design as a guide
  • Increase the resolution as much as possible
  • Improve the logic design
  • Complete the whole thing on breadboards
  • Learn how to create a final PCB version and have it fabricated

Clock & power

Parts:
O20M000000L045 CRYSTAL OSCILLATOR, 20MHZ

I opted for a simple 'clock in a can' the same as Ben Eater, except mine is 20Mhz instead of 10Mhz, because I read the TTL chips would probably just about tolerate it, and I wanted to increase the resolution so I'd need to go faster.
I've never used one before, but it's as simple as hooking up 5v and ground, and the output pin will give a 20Mhz square wave. It also gave me the chance to learn how to use my oscilloscope, as I'd never used one before.
My oscilloscope was showing a pretty wobbly square wave, but that's mostly because I grounded the oscilloscope with a long wire - you'll see this a lot in my pictures.

To power it I made a really quick and nasty 5V power supply by chopping up a phone charger and soldering on a couple of breadboard jumper wires.






Counting

Parts
3x CD74HCT161E: 4-bit counter

To count the horizontal lines I made the exact same circuit as Ben Eater.
I planned on doubling the horizontal resolution compared to Ben Eater's, so the 'H counter' would need to be capable of counting to 528 instead of 264


3x 4 bit counters together are capable of counting from 0 to 4095, so no problems there.
I threw on some LEDs to test it, and used my signal generator to try it out at a slow enough speed so I could see what was going on



Horizontal count logic

Parts
2x SN74HC21N: Dual 4-input AND gates
1x SN74HCT08N: Quad 2-input AND gates
1x SN74AC04N: Hex inverter (NOT gates)

The concept of the count logic (which Ben Eater explains far better than I will) is that there is a horizontal counter, and a vertical counter. I will call them H counter and V counter respectively.

The H counter is increased every time the 20Mhz clock goes high, and needs to "signal" when it reaches the front porch, the sync pulse, the back porch, and the end of the whole line. When the end of the line is reached, the V counter is increased. The V counter has similar "signals" for it's front porch, sync pulse, back porch, and end of frame. For the H counter, these numbers would be 400, 420, 484, and 528.




The counting itself is easy, it's the "triggers" at those specific numbers which is a little trickier.
BenEater's solution was to compare 8 of the bits of the counter via an 8 input NAND gate. He has one for each of the numbers we're looking for.

As a programmer, I couldn't help but see some improvements to this design to make it more efficient, so I decided to optimise this slightly because:
  1. I could reduce the chip count
  2. I'm lazy and it would reduce the number of wires I'd have to hook up
  3. I didn't have any 8-input NAND gates and was too impatient to wait for some to be delivered
My counts are also different to BenEater's, which meant I could improve efficiency quite a bit. Let's start with the easiest one.

528 (end of line)

Here is the binary (what each pin in the counters will read) for 528:


At this point the count will start again, which means it won't ever need to count higher. This means I only need to check bit 5 and 10. If both of those are high, then I know I'm on 528. This means I only need a single AND gate for the 528 signal

Common bits

I never need to care about bit 10, since that will never be on when counting the other numbers (as they're all less than 512) - Ben Eater made a similar optimisation to get the 9 bit check down to 8 bits.

The numbers I need to check for have quite a few bits in common. Since I had some 4 input AND gates, I started by finding which 4 bits were in common between the numbers:


The programmer in me wanted to store the highlighted bits in a 'variable' which can be reused when checking for each number.

If I hook up the green bits (obviously with the 0's inverted where needed) to a 4 input AND gate which we'll call 'A' and the red bits into a 4 input AND gate which we'll call 'B', then the logic table gets simplified to:


I then added another 4 input NAND gate for the 7, 6, 5 and 4 bit check for 400. Let's call that 'C'. The logic table is now:


At the time of writing I don't know why I chose to 'group' bits 4 to 7 instead of 3 to 6, it seems like the logic table would be much prettier, but it doesn't really make any practical difference.

At this point, if I had some 3 input AND gates, I could just AND the 3 items in the truth table together to give me my final output - but again I didn't have any to hand, so I did the same process again, grouping common bits. Since 420 and 484 both check A and B, I fed those into another AND gate which we'll call 'D'. Now I can get 420 and 484 with this truth table:


To summarise each gate (with ! indicating the signal needs to be inverted):


Note that each 4-input AND gate chip has 2 gates, and each 2-input AND gate chip has 4 gates, meaning the total chip count is 3 instead of Ben Eater's 4.

To verify my logic, I made a spreadsheet containing all the possible counter outputs, along with the above logic, which confirmed it was correct. I did most of the planning on paper so unfortunately I don't have a clean logic gate diagram to share.

This design may seem complex, but I was happy to have reduced the chip count, and it allowed me to continue while waiting for the 8-input NAND gates to arrive. This method has some small drawbacks (I kept forgetting BenEater's uses NAND gates, so the 'signal' for each number is inverted), and some large drawbacks, which I will explain in the next section

H Latches & noise problems

Parts
1x SN74AHCT02N: Quad 4-input NOR gates

Having a momentary signal which occurs at the front porch, and another at the back porch isn't actually of any use - they need to be used as a trigger to 'latch' the signal high or low. The implementation of this is explained in Ben Eater's video, so I won't go into depth on that. It's just a simple SR latch except with NOR instead of a NAND since my signals are the inverse of Ben Eater's.

This should have given me correct horizontal signal timings, however it was at this point I had my first major problem.



My sync signal was 3.18μs (close enough to the 3.2μs goal), however the overall blanking time (front porch to back porch) was 6.6μs - it should be 6.4μs.

After a lot of trial and error, and much probing, I discovered the problem. It seemed some kind of interference / noise was causing a bit of a ring on the 528 signal, so the latch wouldn't trip correctly. Since I had no idea what I was doing, I just threw down some capacitors across the power rails to try and reduce noise. Eventually I tried a 680pF capacitor between the 528 signal and ground, and magically the problem was resolved.


Remember this capacitor, because it has an interesting role later in the story :)



I highly suspect my crazy daisy-chaining of logic gates is to blame for the instability problems, I'm sure an expert will probably let me know it's something to do with propagation delays or voltage drops and that I shouldn't have over complicated things :)

V signal

Parts
3x CD74HCT161E: 4-bit counter
4x CD74HC30E: 8-input NAND gate
1x SN74AC04N: Hex inverter (NOT gates)
1x SN74AHCT00N: Quad 2-input NAND gate

Riding the high of seeing the H signal running smoothly, I quickly implemented the V counter next.
By now, my 8-input NAND gates had arrived and I decided redesigning the logic was probably not a good idea, and since my vertical counter would be identical to Ben Eater's, I just copied his exactly.

When doing the H logic, due to the large amount of debugging I had to do, I had given up on neat wiring - however the fear of more noise problems due to 'rats nest' wiring scared me into doing it properly again for the V logic.


This is why the completed V and H signal boards looks like Dr. Jekyll and Mr. Hyde made them:



The below image shows that the V blanking time takes 28 horizontal lines, which is correct as per the VGA spec table above.



Bitter sweet moment

This has consumed quite a few hours by now, but the moment had finally come to hook this up to a monitor and see what happened. With a fire extinguisher nearby, I connected the blue signal to one of the counter outputs (not even through a resistor, which was stupid, but thankfully didn't end up harming anything), and plugged in an old monitor.




The feeling of joy one gets when they finally get their first "reward" from a project is hard to describe; I know it's just some blue lines on a screen, but they're my blue lines and I was proud.

Naturally I did what anyone would do, and started playing with plugging in the R, G and B wires into various counter outputs.



My happiness was soon cut down though. Some of the counter outputs worked fine, but others would instantly cause the monitor to shut off. To make matters worse, stability was generally not great - the monitor would flicker on and off depending on how I moved the breadboards. I clearly had more noise issues.

After adding more capacitors across power rails, I decided the issue must be either the rats nest of wires, or the breadboards themselves. So I set about rebuilding the entire thing. After completing a duplicate H count circuit, I still had the same problems.

My next theory was that 20Mhz must just be too much for breadboards to handle, so I decided to abandon the breadboards, and move onto PCB fabrication.

I had hoped to build the entire thing on breadboards first, then fabricate a 'final' PCB, but since I was stuck, I had to compromise.

You must learn to walk before you can run

I watched a quick tutorial video my GreatScott! on YouTube on the basics of using EasyEDA to design my PCB. EasyEDA appealed to me because it had direct integration to JLCPCB, a Chinese PCB manufacturer who GreatScott! also recommended.

When looking at the JLCPCB website something caught my eye. They offered a full SMT assembly service, meaning as long as you pick from a specific set of components, a robot will solder them on for you. Excited by this notion, I decided to design the whole PCB with surface mounted ICs (SMD).

Here is where I started a long string of failures:

While I was drawing up the schematic, I decided to save myself some chips by changing the 3x 4-bit counters for a single 14-bit counter. Turns out this was a huge mistake, as I didn't read the datasheet properly - this chip does not have outputs for bit 2 and 3, rendering it useless for this project.

Second mistake I made was somehow in the ordering process, the vertical count chip was removed (oh well, it would have been useless anyway!)

Blissfully unaware of my failures, I waited for the boards to show up.





Obviously I noticed the missing V counter, but overall I thought they looked pretty rad, and that it would be no big deal to learn how to solder SMD components. I plugged one in to at least probe the parts which were present, and discovered my third mistake.

I can't stress enough how important it is to actually read data sheets. Remember these are all new chips I hadn't used on the breadboards, and arrogantly I had skipped over reading the 20mhz oscillator datasheet. Well, the one I got was only rated for 3v, not 5v. So one of those boards is dead now...

Switching to 3 volts, I probed the counter, and that's when I realised the counter problem mentioned above. I vowed to never buy a component again without checking the datasheet - this is probably obvious to most sensible people...

PCB V2

After the epic failure of my first try , I went back to EasyEDA and tried again; this time sticking to the chips I knew, making everything through-hole instead of SMD so I could supply my own components, and fix anything I needed to. To double down on my chances of eliminating noise, I also added bypass capacitors to every chip.



I soldered on sockets for each IC for easy removal, but skipped the bypass capacitors just to see it working. And...


It didn't work. Remember earlier I mentioned I kept forgetting I was using AND gates for the H signals as opposed to the NAND gates Ben Eater was using? Well in my schematic I was incorrectly inverting it before sending it to the latch.

Luckily at this point I was no stranger to hacking my way to victory, so I just broke the legs off of the inverter and bridged the gap with a jumper so the signal was no longer inverted.


As soon as I did that, everything was working just the same as my breadboard. 
However, to my despair, I still had stability issues. It wasn't as bad, but when I hooked up certain counter outputs to R, G or B, the sync pulse broke. Even after adding all the bypass capacitors, I was still experiencing the same problem.

Then I remembered that little hero capacitor on my original breadboard. So I soldered a capacitor to the underside of the latch:


And this little guy saved the day. The signal was solid as a rock, and I could tap into any of the counter outputs without any problems.
 

EEPROM and output

Parts
1x AT28C256-15PU: 32kb EEPROM
2x CD74HC541E: 8 bit buffer / line driver
1x SN74AHCT32N: Quad 2-input OR gate


Finally I could work on the bit I was looking forward to: take the counter bits, feed them into the address lines of an EEPROM, and output colour information.

The max resolution the counters would be able to support is 400x600. If I wanted a byte of colour data per pixel (256 colours), then I would need 240,000 bytes of data.
I thought that would be no problem because I had a 256k EEPROM chip. Then it dawned on me. This isn't 258kBYTES, but 256kBITS.
This means I actually only had 32k of addressable 8 bit outputs, so I would need to decrease my resolution - a lot.
If I wanted to keep 8-bit colour, I'd have to make the exact same compromise Ben Eater did and drop down to 100x75. I was determined to make the world's 2nd worst video card, so I decided I would come up with some way of splitting each byte into 2, giving me 4bit colour (16 colours) but an overall possible resolution of 200x256. This still means doubling my clock speed was pointless (for now).

The design I settled on was to feed the counts into the EEPROM address lines in a similar fashion to Ben Eater, dropping bits as necessary (H1, H2, V1 and V10) , but then feeding the bottom 4 bits, and top 4 bits of the EEPROM output into 2 separate buffers. One of these buffers enable pins would be hooked up to H2, and the other !H2 (inverted H2).

The outputs of the buffers then go through a simple voltage divider (again, see Ben Eater's video for details), only instead of 2 bits per colour, there is 1 bit per colour and an additional bit which they all share. This gives a total of 16 colours.

I also used an OR gate to disable the EEPROM when the V or H blanking is active


With my hybrid monstrosity complete, I tried this test image out:


And got this



It looks bad, but it was clearly doing something. I double checked the software I wrote to generate the data for the EEPROM, and finally, after all my efforts, I reaped the rewards



The wrapping at the bottom was predicted, as I've dropped the highest bit from the V counter, so after counting to 256, the EEPROM (unaware of the bit representing 256 now being on), loops back round to 0. So technically this is 200x300, except the bottom 46 pixels are a copy of the top 46 :)

For comparison, here is the source image


The thin black lines are due to EEPROM access time, again explained in Ben Eater's video. It's ugly, squashed, and repeats itself, but I'm still proud to have made this, and happy to have learned from all the mistakes along the way.

Side quest

I wanted to see the benefits of my higher clock speed to see an even higher resolution. So I made another quick 'sister board' to handle the EEPROM access, this time using additional AND gates to have 4 pixels per byte, i.e. 2-bit colour. This would allow me to have a resolution of 400x256 but only 4 colours.

This was the source image and output:





As you can see the access time limits are really being pushed and so the result is really not great.

Final PCB

With everything now working, I wanted to get a final PCB (and fix the bugs I hacked around with the first one).










I wasn't a huge fan of hooking up an external power supply via jumpers, so I hacked a little micro usb breakout board onto it so it could be powered by micro usb.





One final mistake I made on the PCB was mixing up the Blue and Green outputs. I simply fixed this on the software side





Overall the project took about 2 months (a lot of it spent waiting for PCBs or components to arrive).
I learned so much, and thoroughly enjoyed myself. I keep the finished card on my desk and every now and then just look at it with pride that I actually finished a hobby project for once. My wife even surprised me with a framed picture of the board layout for Christmas


The future

I am interested in picking up Ben Eater's 6502 computer project in the future, and would then love to hook it up to this. If I replaced the EEPROM with SRAM, the picture would be a lot better (no black lines), and in theory I could also upgrade the RAM size to make use of the full 400x600 resolution the card is capable of, with full 8 bit colour.

If anyone has any specific questions they want me to answer, please let me know
Good luck with your projects and thank you for all the positive comments I've been receiving


Comments

Popular posts from this blog

Upgrading my BenEater inspired video card

BenEater Inspired GPU - Final Design