It’s been a while since I presented the vision for controlling FPGA logic. Since that time, we’ve put a lot of work into building an FPGA controller that we can use for debugging logic running within our FPGA. [1] [2] [3] [4]

Our goal over all is to provide the student, or any other junior FPGA designer for that matter, with a tool, similar to what an expert might use, that he can use to figure out what is going on within his design.

Today, let’s take a look at what that the software side of communicating with an FPGA might look like. In other words, once we connect to our FPGA, how will we control that FPGA from software?

A Simple FPGA Software Controller

The simplest FPGA control program that I’ve built is the one I call wbregs. This program just reads (or writes) a single register within the FPGA. The source code for this program makes it look more complicated than it is though. That’s really nothing more than the fact that it’s been lying around long enough to be improved upon one too many times … but I digress.

Lets trim this program down to its bare minimum details, and see what it would take to control an FPGA using this sort of controller.

The first step in my idealized world would be to declare a global value to hold a pointer to my FPGA’s interface:

FPGA *m_fpga;

Then, somewhere within my main program file, I’d want to connect to my FPGA. If the FPGA talks to the computer via a serial port, I might wish to say something like:

const char SERIAL_PORT[] = "/dev/ttyUSB0";
m_fpga = FPGA(SERIAL_PORT);

We may need to come back to the name later, in case we have more than one USB serial port connector, but for now let’s just move on.

I’d really rather connect to my FPGA from any one of my computers via a network port, as in:

const char FPGASERVER[] = "fpgahost.name"; // Could be 192.168.7.1 ...
const int FPGAPORT = 2853; // Some random number, known to you
//
m_fpga = FPGA(FPGASERVER, FPGAPORT);

Wouldn’t it be nice if that was all it took to connect to an FPGA?

How about reading the a status register from the FPGA? For example, we might wish to read from the erase control register of the flash controller to see if it is still busy. In that case, it might be nice to write something as simple as:

if (m_fpga->readio(R_FLASH_EREG) & FLASH_BUSY)
	...

The same could be said about writing to the erase control register, in order to command the flash to erase a segment of flash memory–but that’s a topic for a later day.

For now, what about that scope? Wouldn’t it be nice to be able to reset the scope for another capture? The easiest way I can think of doing that would be to write the new hold off to the scope control register, as in:

m_fpga->writeio(R_SCOPE_CONTROL, 25); // New holdoff is 25-clocks

Or to read from the internal FPGA scope, wouldn’t it be nice to just simply … read?

for(unsigned i=0; i < scope_len; i++)
	m_scope_memory = m_fpga->readio(R_SCOPE_DATA);

You do remember how, in our example scope design, we placed all of the scope’s data at the same register address, right?

We might even manage to optimize successive reads, making a similar command that could capture any optimizations associated with many reads in a row. If so, we could write our for loop that read’s from the FPGA into something that could be optimized, such as:

m_fpga->readz(R_SCOPE_DATA, scope_len, m_scope_memory);

Working on Video? One of the hardest parts of working on Video is getting that video information in and out of the computer. Unlike the scope, your video information is going to occupy successive addresses, rather than a single data address.

Wouldn’t it be nice if you could read from your video RAM area exactly what was being transmitted, as in:

uint32_t	m_video_image[NUMBER_OF_PIXELS];
...
video_size = NUMBER_OF_PIXELS;
m_fpga->readi(R_VIDEO_RAM, video_size, m_video_image);

Then, when we are done, we should close our FPGA interface via a C++ destructor such as:

delete m_fpga;

Could we make controlling an FPGA that easy? The answer is, Yes. Yes, we can. Not only can this be done, but wbregs does it. Sure, wbregs has a lot more code for error handling, and I just added code to look address names up from linker map files, but simply reading and writing from an FPGA is what it was designed to do.

This will only hurt once

Even better, after you go through the pain of building just one debugging controller for your FPGA, you can then use it for every one of your FPGA projects.

Think this one over: the debugging interface isn’t going to be graded. (Ask your instructor if you aren’t sure of that.) If that’s a problem, then pull it out of your code before you turn it in. Either way, if you use a debugging bus in your design you will have a leg up over all of your classmates who do not.

Now, consider this sort of interface from the standpoint of the FPGA expert’s design process. He’s going to build this sort of interface once, and then he’s going to use it in all of his projects. Some time later, he may come back and add a little bit of capability to it, but he’ll still be using it with every project.

Perhaps an example from my experience would help. My first design was for a Basys-3 development board. That’s where I build the first version of my own FPGA debugging interface.

  1. After I built that first interface (it was similar to the hexbus interface we are building here) I became frustrated with how slow it was–particularly when I was using it to read and write new configurations to the flash.

    So, I added compression to the interface: both encoding and decoding.

  2. More recently, I got frustrated with the fact that my interface used up a serial port, preventing my CPU from printing to the same serial port.

    In this case, I restricted the debugging interface to 7-bits, and used the high order ASCII bit to determine if the interface was communicating printable ASCII (such as the CPU with a printf), or unprintable characters (such as the debugging interface). Since I was only ever using 7-bit bytes for the debugging interface in the first place, this change was easy to do, and it then gave me two channels of data that I could run through my serial port.

My point is simply this: You need to pay for your tools. Pay the time and energy to build them once and build them well. Now that they are built, use them in all of your projects.

Let’s even go one step farther: I’m going to license this hexbus student interface that we are building under the LGPL. What that means is that I’ll let you copy it into your project, without being required to post the rest of the code for your project. If you improve upon it, post your improvements. If you are willing to assign ownership of those improvements to me, then I’ll post them back into the directory for others to use.

Once we’re finished, then you, as a student, can play the role of the expert. You too can come to the FPGA design problem with a debugging tool suite under your belt.

This is why I pointed out, when discussing how an Expert really does FPGA design, that he comes to the problem with a lot of tools already in his toolbox.

Just a simple vision

All of our work here is simply towards one single goal: to build an interface to your FPGA that will then simplify all of your interactions with that FPGA. Such an interface allows us to write commands to the FPGA and to read status back out of the FPGA. It will allow us to develop and prove that peripherals work, and it will help us to find out why they do not work when they do not work.

This is the component most students don’t realize they are missing within their design.