CPU bus, new registers

A project log for Diapason : m68k homebrew with FPGA graphics

An m68k homebrew with an FPGA acting as a VGA graphics controller

Carson HerringtonCarson Herrington 07/20/2019 at 08:380 Comments

This log is going to focus mainly on the FPGA core, as I am still having some teething issues with the hardware. I'm fairly sure the Altera USB Blaster isn't going to play nicely with it until I put pull-up resistors on a few of the MAX10's configuration pins. I have yet to get around to doing this, but do have some good progress on the FPGA core to show.

Host Bus Interface

Finally, I've implemented a separate bus for the host CPU to communicate to the controller with, as seen in the following verilog code snippet:

The bus is asynchronous, and is updated at the 50MHz core clock.  I chose an async interface mainly because a synchronous bus is a huge pain to debug when it inevitably fails to transfer correctly. vga_ack is the transfer acknowledge output, which is set as soon as the controller realizes it's being accessed, and unset after vga_select is unset by the host.

The verilator testbench has received updates to test this functionality as well. It keeps track of what pins to change each cycle by keeping track of the total number of bus cycles, then just doing something different each time. The count number is also abused a bit; it is set arbitrarily a few times to force the next bus cycle to start something entirely different.

Here's the current list of registers accessible through the bus. The function of each register is described either by the blindingly obvious name, or through the comments next to its definition.


The controller can now generate interrupts! They can be enabled by setting one of the enable bits in register 4. Currently, the only implemented interrupt type is the raster interrupt, but I have reserved space for an interrupt generated at the start of the vertical blanking period. The line that triggers the raster interrupt is set using registers 5 & 6; two registers, one as the high byte, and one as the low byte.

The current raster number is checked every cycle, which means it is able to be triggered halfway through a line. I can't imagine any possible way for that to be useful, but it is good to note.

Scroll Registers

Last project log, I mentioned that I would like to try adding a scroll register; I ended up doing that, both for X and Y. Each register is 8 bits wide, which allows them to move the display data up to 255 pixels in their respective directions. Here's the X scroll reg being set based off the current scanline:

Now, this isn't an entire screens worth of data, as seen above by the sudden resets to 0. If you wanted to scroll any amount past 255 pixels, you would have to manually move the entire brick of VRAM, which can be as large as ≈300KB. If you wanted to do smooth scrolling of the entire screen, that would be pretty abusive to the host device. I'm not 100% sure yet, but I'll probably end up adding an extra register with the 9th bits of each axis so that I can offload as much mindless blitting to the controller as I can.

Does it work?

Yes, and it's only slightly broken! The first thing I tried was using the raster interrupt to change the X scroll register halfway down the screen:

At first look, it seemed to work wonderfully (aside from the very-on-purpose tear in the middle)! It also exposed the first issue: the very obvious error with the first column of pixels. Upon closer inspection, the first column is actually being displayed 1 line early:

After the raster split, the first column also becomes some data I honestly cannot find the source of in the bitmap. Even though this didn't appear previously, I'm not convinced it's entirely new behavior, since the issue goes away if I just set it to bitmap mode and leave it. Even more confusing is that the data actually displayed changes based on the current scroll register value! After an hour or two, I really have no clue where this anomaly is produced. The video mode modules themselves are certainly suspect, and that's probably what I'll focus on, but I'm not entirely convinced the testbench isn't at fault either. Really, I have nothing that exactly suggests that, but it seems best just to be pointing the finger at everything for now.

Back to the tests, I also tested the same thing in 80x30 text mode:

This shows the controller handling the scroll reg changing in the middle of a line of character cells without visible fault. The characters themselves have their first columns completely black, so the previously discussed issue could be present, and I'm sure it probably is.

Now, when raster interrupts are brought to mind, the first thing I tend to think about is changing the video mode in the middle of the screen. Many demos and games on the Commodore 64 use this to great advantage to display text alongside with graphics on the same screen (sometimes splitting the screen vertically, but that's a little difficult to time). Naturally, that was one of the first raster tricks I tried after verifying the thing wasn't entirely broken

This functioned just fine for the most part. Clearly, the actual mode switching worked as intended. Like I said before, changing the scroll register seems to change the actual data being displayed. Again, here we see the same issue of the first line being strange, I can't seem to find the source of the data. This time, we have the added bonus of each piece of data displayed being repeated twice, for some reason. The characters appearing cut off here is a result of me not applying the scroll offset to the fetch address, so that's not unexpected. This issue is a total mystery at the moment.

Speaking of issues, here is issue number 2: it doesn't exist, as far as I can tell. Obviously there could certainly be another issue, but it would have to be somewhere that hasn't been tested, which has the nice benefit of meaning I can just assume it doesn’t exist for now.

With this new set of features, every intended feature works as it was meant to. Now, there is also the presence of a certain unintended feature, but that only appears when doing funny business with raster interrupts, so isn't fatal for normal use cases. Starting now I'm just going to end these things with the features I'd like to experiment with in the future. I have put 0 actual thought into how these would be implemented, but they sure sound cool:

I have created a new branch for development sources, seeing as I may or may not have introduced some bugs with this stuff. That, combined with realizing that having one branch for everything isn't very swell. I have chosen to include the statically compiled binaries this time, should you want to run the simulator and also happen to be running 64bit Linux. (in the root dir, run `./bench/cpp/main_tb -i`)