How to send our bus results back out the serial port
This post starts to complete the design of a very elementary debug port that can be used to command an internal, on-board, wishbone bus. Other posts in this series include:
-
How to build a Simple Wishbone Master, and
We’ve slowly been building up to the implementation of a WB-UART component, such as that in Fig 1.
A this point, we’ve now described how to get a serial port up and running, how to create words from the serial stream, and how to control a simple wishbone bus master from the command words running through this stream. We are very close to having a working debug bus–since we can add the FIFOs and interrupts in later if we have to.
This lesson is about what it takes to turn the response words from the bus master into a characters that will be sent out the output transport. The output transport, as you will recall, has a functionality similar to a serial port. Indeed, it might be a serial port, although it doesn’t need to be.
Stream timing
Before we start, though, let’s examine how to handle stream timing. Our problem is that we will be generating words to be sent out of our serial port at a rate that is much faster than the port can handle. We’re going to want to make certain that these response words are not lost–in spite of the speed differential.
Earlier, we described how to build a strobe type of signal. For our purposes here, the strobe signal was one that was valid for a single clock only. Here, we are also going to use a busy signal. The two signals will work together in the following way: the strobe will indicate a desire to send a word downstream, while the busy response from the next component will be used to indicate that the downstream component is not ready to receive. To deal with this, once we set our strobe we will continue to hold it high (request data be sent) until the downstream components busy but is cleared.
Conditions for moving forward will therefore look something like …
This condition will be true any time we accept a request to send a word.
Likewise, there is a similar condition on the downstream side of any stream component:
Prefixes will change depending upon which side of the link we are on, or which components we are using, but the logic is going to be the same every time.
Now, with that out of the way, we can disassemble our words into a serial port output channel.
Breaking Words into nibbles
The trick to making any stream processing work is to only do a little bit of processing at each step. Hence for our first step, we turn our response word into bytes.
There are a couple primary variables to make this work. The first one is the word buffer, r_word. This buffer holds what is left of our response word that has not yet been sent. The next variable is the r_len word that holds how many valid nibbles remain in our r_word register.
Hence, on any new incoming word, as long as we are not busy, we send a nibble noting the top two bits (Read data, write acknowledgement, etc.), and we note that the register now has eight nibbles within it. In the case of any special words, we instead send the top five bits of the command word and nothing more. Other protocols would work as well, this is just what we are going to choose.
Here’s the big always blog for what we want to do.
We’ll simplify this giant always block by first removing the reset logic from it. Indeed, the only things that need reset logic are the r_len and o_dw_stb registers.
You should find this logic identical to the big always block above.
The rest of our logic, to include all the various wires associated with the word register r_word can then be drastically simplified.
Even the outputs may be set quite simply.
If you look over the difference between what we started with and where we ended up, there’s a lot of differences. All of these differences translate into fewer resources in our output. They can basically be summarized by this: if you don’t care the what the value of a register is under certain conditions, then don’t make that register depend upon those conditions. Remember, the resources used by a piece of logic can be (roughly) calculated by the number of inputs that piece of logic requires.
Turning nibbles into characters
The next step on the output process is to turn this stream of nibbles into output characters. This is almost a pure lookup table, but the stream logic keeps it from being quite that.
The stream logic consists of paying attention to the busy and strobe flags to determine when we have an output to send. In particular, if we are not busy and a request is made of us, we’ll forward it down the line.
Since we don’t have more than a single clock’s worth of processing to do here, we can just set the busy bit to be the same as our output strobe bit.
The real work is in the lookup table. When given a value between 5’h0 and 5’hf, we just send the hexadecimal output. Values with the 5’h10 bit set indicate we need to send a special character. You can see the special characters we support below.
Unknown characters just get quietly turned into carriage returns, which the next module turns into carriage return and newline pairs (CR/NL).
Adding Carriage Returns and Newlines to the Stream
The final step is to add a carriage return/newline pair to our output stream any time it becomes idle. This will assist any buffered software following that might depend upon the end of a line before forwarding the data received.
To do this, we’ll create a new stream component in our processing chain. Any characters given to this stream will just send their characters back out the port. But, if the port is idle we’ll insert a carriage return into the stream. Then, any time a carriage return is sent out, we’ll set a register last_cr to indicate that we’ve sent one carriage return, and don’t need to send it again. This value will clear upon any new and valid data from the port.
The next state variable we’ll use is cr_state. This variable will be set at anytime last_cr is set, and we’ll clear it upon sending a newline.
Put together, any time we enter our newline state, we’ll set last_cr and set cr_state. After the carriage return is sent, we’ll send a newline and clear cr_state. We’ll then stay in that state until anything else is sent through the port.
Future Posts
There, that’s all there is to it! You now know how to take a response word from the wishbone bus master, and how to send that result back out a serial port.
While we’ve just about presented all of the components, and while you almost have a usable bus at this point, we’re not there yet. Some particular functionality remains missing. This includes:
-
Adding interrupt notifications to the response stream
-
Adding idle notifications to let you know you are connected to the right stream
-
Creating a hand-controlled test bench to prove this works
-
Creating a software bus controller
Now that our controller is nearly complete, though, these will be much simpler topics. We’ll come back to them in future posts.
As cold waters to a thirsty soul, so is good news from a far country. (Prov 25:25)