Building a Simple Wishbone Master
Explaining how to build a good wishbone controlled debug port may take a couple of posts to do right. Worse, it may take us a couple of rounds just to get the logic right, but let’s try anyway.
For this post, we’re going to concentrate on the wishbone bus master found at the bottom of the simplified UART to wishbone converter, as outlined in Fig 1.
Hence, if the whole capability will eventually look like Fig 1, we’re only looking at the component at the very bottom within this post, and even then we’re only going to examine a simplified version of it. We’ll leave the implementation of multiple transactions at once for a later date.
My strategy for this blog post will be to come back and update it later with any updates or fixes, so (hopefully) any mistakes will get fixed over time.
We are going to try to take this opportunity to build a simple wishbone bus master. Before starting, you may wish to grab a copy of the wishbone bus specification and follow along in the B4 version.
You can find definitions for the various wishbone interface wires in chapter two. We’re going to continue following our practice of prepending input wires with i_… and output wires with o_.., even though the specification appends similar notes. We’re also going to add the _wb_ designator to all of the inputs associated with the wishbone bus. Hence, i_wb_ack will reference the return acknowledgement from the slave.
Chapter three of the spec describes how the various wishbone wires compose a bus cycle. We’ll specifically be implementing the pipelined bus cycle (not the classic bus cycle), and we’re eventually going to build our implementation so that it can issue requests across the bus in a pipelined fashion as fast as the slave will allow it.
If you are a visual learner, check out figures 3-6, and 3-8 from the specification. I find these to be the most useful, as I’m explaining how the bus works. We’ll reserve the capability shown in Figures 3-11 and 3-13 for a later post.
For today’s post, we’ll handle a single bus interaction per bus cycle, so the CYC line will be lowered between requests. We’ll revisit this decision in a later post so that we can issue multiple requests of the bus at one request per clock, but that will be a later discussion.
Further, you can see the code as we’re building it in the dbgbus project here.
The Control Interface
The bus master interface we are building will ultimately be commanded from an external interface. We’ve already discussed how that one might wish to do this over ICO board parallel port, UART, SPI, or JTAG. Exactly which is used will be external to our bus master implementation.
We’ll have to build up the functionality in our control interface as we build up the whole interface. The two sort of go together.
For now, let’s handle all of our communication using 34-bit words. We’ll use the top 2-bits of these 34-bit words for signaling, and then the bottom 32 for any values we wish to pass.
Our input data word will be valid any time i_cmd_stb is valid, but it will only be accepted whenever o_cmd_busy is false. Hence, when you read the code, you may find (i_stb)&&(!o_cmd_busy). This will be the indication a request has been accepted.
For now, we’ll just set o_cmd_busy to be true any time the bus is active. Eventually, we’ll want to take a peek at the next bus request, and drop o_cmd_busy if the next request is one we are interested in. For example, if we are busy doing something and the user requests a bus reset, we’ll need to drop the busy line and accept that request.
For our command words, we’ll use the following definition to define how the 34-bit control words will be interpreted:
33 | 32 | 31 - 0 |
---|---|---|
0 | 0 | Read request, ignore the rest of the 32-bits, ACK on output |
0 | 1 | Write request, the 32-bit data contains the word to be written |
1 | 0 | Set an address. If bit 31 is set, we’ll add this value to the current bus address. If bit 30 is set, the address will be incremented upon each bus access |
1 | 1 | 4’h0, 28’hxx, Bus Reset |
Our signaling scheme will allow us to issue a bus reset command, which will abruptly cause us to abandon any bus cycle we may be in the middle of. To make this work, the bus reset request will need to override the busy flag.
This is not the most efficient scheme. For example, why send 34-bits when you are only going to pay attention to two of them (i.e. the read)? Wouldn’t it make more sense to send a smaller number of bits for the read, together with the number of items you intend to read? Yes, it would. Optimizing this command word will be the subject of another post.
The output of our bus will use (almost) the exact same approach. We’ll create a o_rsp_stb signal that will be true any time the output references a valid codeword. Unlike the input, though, we’ll ignore any flow control on the output. We can add some amount of flow control back in later with a FIFO.
We’ll also borrow from the input codeword encoding for the return trip encoding, as shown below:
33 | 32 | 31 - 0 |
---|---|---|
0 | 0 | Acknowledge a write. The 32-bit value contains number of writes to acknowledge |
0 | 1 | Read response, the 32 data bits are the word that was read |
1 | 0 | Acknowledge an address that has been set, with two zero bits and 30 address bits |
1 | 1 | |
1 | 1 | 3’h0, 29’hxx, Bus Reset acknowledgement |
1 | 1 | 3’h1, 29’hxx, Bus Error |
Decoding the Control Interface
We have only four different types of command words in our code book. In the first section of our simple bus master, we’ll create flags to indicate which request is currently being made.
You may notice that I have violated my naming convention with these wires: I have named locally generated wires with an i_… prefix when they are not actual inputs to our bus master module, but rather the results of combinatorial logic applied to inputs. In this case, it tends to work out, but it’s not something I’m regularly going to do.
The Wishbone Master Interface
This bus controller will have three basic states, as shown in Fig 2.
Here’s a quick description of each of those states:
-
IDLE: When we are doing nothing, both CYC and STB must be low. In this state, we’ll need to be responsive to incoming requests from the bus. Upon a request, we’ll need to set the request direction (o_wb_we), the data lines (o_wb_data) and then CYC and STB.
We’ll also set our address lines in this state, but without adjusting CYC or STB.
-
BUS REQUEST: When CYC and STB are both high, a bus request is taking place. This request phase lasts until i_wb_stall goes low, at which point our request has been accepted.
When we come back to this code later and transition it to handling multiple requests, we will transition from one request to the next any time o_wb_stb is true and i_wb_stall is false.
It is also possible that the acknowledgement might be received on the same clock the transaction was requested. We’ll need to make certain we deal with this case.
-
BUS RESPONSE: After making a request of the bus, we need to wait until the slave acknowledges it. Every acknowledgement will lead us to sending another response across our command interface back up our command stream. More importantly, every read response will also need to carry the value read from the i_wb_data data lines in its payload. We’ll need to make certain we return those back to the user.
Once the last acknowledgement is received, we can transition back to the idle state.
There are a couple of exceptions to this model: if a bus error occurs, we’ll simply abandon the current transaction. This approach has the inherent problem in it that acknowledgements may come back later and get mixed with another bus request. For now, we’ll accept that risk and do it anyway, since it may be the only way to recover the bus if a peripheral is non-responsive.
Simplified Overview
If you’re still somewhat new to digital design and coming from the software world, your first approach to building a Wishbone Bus Master might look something like the following:
From this view, the three states of the controller should be readily apparent. The controller starts out idle, CYC=STB=0. Once the controller receives a command, it moves to a bus request state to issue the command, CYC=STB=1. Once the command has been issued, but before any response, it is in a bus wait state with CYC=1, STB=0. When the final ACK comes back, we’ll go back to idle, CYC=STB=0.
That’s how a wishbone master works.
When I first sketch out a design, it often looks very similar to this giant always block above. Perhaps it’s my software background. I like to build one big always block with all the parts and pieces within it. Indeed, my flash controller is still built in this fashion, with one giant always block.
Now that I’ve been doing this for a while, I’ve learned that breaking the big always block up into little blocks is easier on the FPGA. For example, in this case, why should the address lines only get set when the new address command shows up and the reset is clear?
For this reason, we’ll split up the always block into parts and pieces.
The CYC and STB Lines
The wishbone CYC and STB lines are so integrally connected, they tend to remain together no matter how the interface gets broken up. Indeed, these two wires alone define which state we are in within our state space. Further, in the big always block above, few lines actually depend upon the reset line. Hence, we’ll build their state diagram like this:
The write line
We can significantly simplify the output bus write enable line. Since we only accept commands when we are in the idle state, and we only transition to the bus request state on a read (or write) command, we can just simply leave this as:
Notice how much we just simplified this.
The consequence of this simplification (and the many others like it) is that our code will be harder to read. The positive: because this line now uses fewer FPGA resources, there will be less logic between clocks, allowing you to (possibly) run your clock a touch faster, and there will be fewer LUTs used to generate this line, allowing you to place more of what you care about onto your FPGA.
The Address Lines
We remove the address lines from the big block simply because there’s no reason why the address line logic needs to depend upon the reset line. On reset, we can allow the address (and the increment) to both come up undefined. We’ll also keep our own internal new address variable in this block as well.
The output data lines
Those output data lines can be set just like the write enable line. Only, this time, we don’t care what the lines are set to when we are reading. Hence, we’ll set them upon any request.
Since we are setting 32 outputs, the logic savings is much greater than the savings from simplifying just the one o_wb_we line.
The output result
We also need to return a result back up the command chain. This result will be dependent upon what has taken place. It could be:
-
An acknowledgement of a bus reset request
-
A notification of a bus error
-
An acknowledgement of a new address, or a value that has been written
-
Or (finally) the result of any data read from the bus.
Because of all of these possibilities, it takes a bit of logic to set this right. Remember, o_rsp_stb will be true any time o_rsp_word has valid information, and that the o_rsp_word wires are don’t cares any time o_rsp_stb is low.
A Not-So Simplified Wishbone Master
If you’ve along so far, you may notice we’ve left a lot of capabilities we want in our bus master on the floor:
-
There’s no means for sending multiple write commands without dropping CYC between them. This will break our flash controller, so we’ll have to come back and fix this.
-
There’s no means for reading from (or writing to) multiple consecutive addresses in one transaction. That’s really useful for getting us on and off the bus quickly. We’ll need to come back to this later.
-
There are a lot of times when the bits in our 34-bit codeword are going unused. For example, why transmit 34-bits to our device just to send a reset, when only six of those 34 bits are ever used to decode a reset? Why send a 30’bit address offset, when you are just adding it to the current address?
If we want a really good wishbone master interface that’s fully featured, we’ll need to come back and fix these things. For now, let’s move on to the next piece in our command wishbone bridge.
Examples
Now that you know the basic pieces of any wishbone bus master, here’s a list of some example wishbone bus master’s that I’ve built that you might find worth referencing:
-
Instruction fetch: one word at a time, two at a time, and using a cache.
-
CPU memory stage: one word at a time time, and pipelined.
Next Steps
You can find prior posts in this series on the site topics page. You can also see from that page where I’m hoping to go next.
Servants, be obedient to them that are your masters according to the flesh, with fear and trembling, in singleness of your heart, as unto Christ (Eph 6:5)