Moving to memory
There seems to be a difficult transition between the beginning FPGA designer, and the more experienced designer. From the posts I’ve watched and replied to, this transition appears to be centered around accessing memory.
Before Memory
Usually, a beginner’s FPGA work consists of very simple tasks: building a serial port, controlling LED’s, making things blink, and so on. These tasks can seem fairly simple, and they can usually be solved with only some simple Verilog.
Once the beginner has successfully completed these tasks, they then try to move on to something real. For example, maybe they want to record some video, capture images from a camera, or even to transmit a song over a FM radio peripheral. Every one of these tasks, though, requires memory. These beginners quickly discover that … there’s just not that much block RAM within an FPGA.
What these beginners want is to continue their learning process just like they’ve done it up to that time.
This is where they get stuck.
Why is memory different
Up until this point, when building for an FPGA, they can access as many memories as they want within their FPGA. When they need a memory, the just connect it to their circuit.
Now, when they need a big memory from an external circuit, … there’s only one memory pipe. Anything that needs to access that memory, needs access to the one and only memory pipe’s controls. The controls look much like any bus:
- Given an address and a read strobe, return the value at that address.
- Given an address, a data value, and a write strobe, replace the contents at that address with the new data value.
For this, you need a bus.
Many different bus standards exist. There’s the Wishbone, the B4 version of the Wishbone that I like and use, there’s the B3 version used by the OpenRISC team, there’s the AXI bus used by Xilinx and ARM, etc.
By itself, using the wishbone bus is pretty easy. AXI is harder, but once you understand how a bus works its not that much harder. We’ll come back to that later with some easy examples of how to use one.
Beginners should get used to busses
Before trying to read from memory or building a memory controller, a beginner should learn how to read and write from a bus. Lots of simple things with busses are really appropriate beginner tasks.
For example, a very simple piece of bus logic is a bus arbiter. Since only one piece of logic can access the memory at any given time, if you want two pieces of logic to be able to access memory, you need a bus arbiter. (See here for a wishbone example.)
A classic example of this would be a project that reads from a camera port and writes the result to memory. If you ever want to get that image out of memory, you will need an arbiter controlling which logic has access to the bus.
As another example, if you are going to build a bus, why not put some debug logic on that bus? It’ll make getting your memory interface up and running later a whole lot easier.
The simplest bus to get used to is one where every item on the bus has exactly the same timing. A simple debug port might be like this.
A harder bus is one where requests must be held on the bus until the respective peripheral is ready to read them. (WB B3/Classic)
Harder still, one where many requests may be made before the first result comes back, some variable number of clocks later. (WB B4/Pipeline)
The AXI bus, though, usually takes the cake for complexity. This bus has separate channels for read requests, write requests, write data, read data, and the write request acknowledgement. Each of these channels has bi-directional flow control so that requests may have to wait for the peripheral to be ready. Worse, this bus may respond out of order to various requests. Ouch!
Actual memory controllers are complicated
Finally, once you are comfortable using a bus, only then is it time to connect it to a memory controller.
This is also where the beginner punts and looks for a prewritten memory controller—usually because he has never learned how to deal with a bus, or he’s read the memory controller spec sheet and … decided it wasn’t worth his while.
These beginners will also tend to switch to the AXI bus, not because they know how to use it, neither because it’s the best bus out there, but because Xilinx has provided them with a memory controller for their memory chip that uses the AXI bus.
This is usually also the time in their development where they abandon the Verilog or VHDL code they’ve been working with in favor of one of the visual and graphical tool flows.
This leads to a bigger problem down the road: they’re stuck using the Xilinx IP because 1) building something that interacts on an AXI bus isn’t simple, and 2) because it’s difficult to integrate their special sauce into the official IP integrator. Indeed, the problem is so complicated that AXI has to offer a special subset of the AXI bus specification, AXI-lite, just to try to make this easier.
Stick around, and I’ll show another way
Having now written several memory controllers, I understand they can be difficult. They are also, however, quite possible to build. The beginner who abandons their learning process for a set of ready-set of tools, however, will quickly loose the fortitude they need to get there.
If you stick around on this blog, I intend to show you another way.
For precept must be upon precept, precept upon precept; line upon line, line up line; here a little, and there a little (Is 28:10)