I love when people share their code on the internet, but have a complaint: short, obscure variable names. Also, a lack of whitespace. Most engineers aren’t the best at documentation, myself included. But I try my best to make my code very readable. It can be a pain type out 20 character variable names, but that’s where code completion, along with regular expressions and find & replace come in.
Surprisingly, unreadable code recently led me to do something I don’t often do: start from scratch. I was looking for a Verilog module to generate an NTSC signal, which is the analog television standard in North America and a few other places. There were plenty of VHDL snippets out there, and one or two Verilog, but I couldn’t make heads or tails of what the author was doing with it. So I got on Google and and found just about every document I could on the NTSC standard until it finally sunk in. I was just starting to get the hang of Verilog before I started this project, but having to go from timing specifications to an FPGA (Field Programmable Gate Array) really made it all sink in. The irony is that I really didn’t even want to generate a TV signal. I wanted to output video from my prototyping board to a VGA display, but I don’t have one. Normally they’re a dime a dozen on trash days here in NYC, until I needed one. But, I do have a small TV a neighbor threw out a few months ago.
So here I’m sharing my project. At the heart is a file named interlaced_ntsc.v
, which generates 3 bit monochrome video signal. There’s one bit of code I borrowed, another file named 8x16_font_rom.v
. It’s just a pixel lookup table for all the ASCII characters. Making a font rom is tedious, so I don’t feel bad about using that. The top project file is top_ntsc.v
. It’s included only as reference to complete working project. You don’t need to use it unless you’re new to FPGA’s or Verilog.
Here’s the instantiation template for my NTSC code. There’s a few signals not listed, which are only needed for debugging with an oscilloscope. They’re documented in the code.
interlaced_ntsc.v
instantiation template
interlaced_ntsc instance_name (
.clk( clk ), // input: 50 MHz clock input
.pixel_data( pixel_data ), // input: brightness of pixel 0..5
.pixel_y( pixel_y ), // output: current pixel's y coordinate (row number)
.pixel_x( pixel_x ), // output: current pixel's x coordinate
.pixel_is_visible( pixel_is_visible ), // output: high when not a sync signal, but not necessarily visible on the screen
.ntsc_out( ntsc_out ) // output: the TV signal, fed to a 3 bit r2r DAC
);
The 50 MHz clock requirement is baked into the code right now. I’m trying to figure a way to improve that. If you need to use a different clock, adjust the timing constants in interlaced_ntsc.v
. They’re pretty obvious.
That’s it. I’m the first one to admit that I’m frequently a hypocrite, so if there’s something you don’t understand, or needs more comments, please let me know. And if you see a way to improve it, definitely leave a comment.