I need to remember how valuable writing is as a thought tool. The other night I was truly stumped as to how to solve my VGA problem.
As I was finishing writing my previous post, I began to get an idea of where to start debugging my problem. I even said so in the post:
Remember, there’s a break at the end of every line. This means that I need to start loading the character data for the start of the next line at the end of this blank period. There’s also a longer break at the end of each screen. This means that I need to load the data for the first character of the screen at the end of this blank period.
I think this is where the bug is. All the characters in the first column are one half-line (at 320x240) lower than the rest of the screen. My best guess is that I need to start the data load a little sooner during the blank period.
So, I looked for the code that calculates which row of pixels to load, and I found this:
wire [11:0] rom_adr = { pattern_num, vga_vpos[3:1] };
This line says, “the location in ROM is composed of the pattern_num
(i.e. the ASCII
character) and bits 3, 2, and 1 of the current line”. Apologies to anyone still
reading this that isn’t familiar with binary numbers, but those details don’t really
matter. What matters is what I put in bold above. We are using the value of the
current line to calculate the value of the next character. This works for every character
in a row, except for the first character.
When we read the font data for the first character in a row, we are using a value based on the previous row. We need a special case for when we read the first character.
This is what I ended up with
wire [10:0] rom_adr = { pattern_num, (display_on ? vga_vpos[3:1] : next_rom_row) };
Basically this says that if the display is on, use the current line position. If the display is off, (i.e. the blank period at the end of the line) use the next line position.
There was some other work I had to to do calculate that value properly, but it works now!