We’ve slowly but surely now built up all of the pieces necessary to build a debugging bus to provide wishbone master access into an FPGA.

Fig 1: WB-UART Overview
Block Diagram of a Simpler Wishbone to UART converter

As we’ve done so, we’ve gone through several posts to get here describing all of the various components. Here’s a list of those previous posts in this series, in case you wish to start at the beginning and go through this development.

Today’s post is about putting all of these components together. When we are done, we’ll have a working debug bus that you can use to read and write from a wishbone bus within your design.

In this design, we have very few module level requirements. Our big goal is little more than that we put all the pieces together, as outlined in Fig. 1 above. Hence we’ll jump right into putting the pieces together.

Putting the Pieces Together

You can find our various pieces so far here.

As we put these components together, we’ll skip for discussion purposes the register and wire definitions, as well as the module interface definition. If you’d like to see all of the pieces, including these definitions, please feel free to examine the final module code here.

The first component will take our incoming stream and turn it into 5-bit data words having a binary representation.

hbdechex dechxi(i_clk, i_rx_stb, i_rx_byte,
		dec_stb, w_reset, dec_bits);

Note that it takes a module level input, i_rx_byte, containing the received byte, together with a strobe, i_rx_stb, which will be true for the one clock when i_rx_byte is true. In a similar fashion, the outputs bits will be placed into dec_bits, which will be valid any time dec_stb is true.

One unique feature of this component is the w_reset signal that will be true for one clock, on the clock after ‘T’ was received. We’ll use this signal to reset all of our processing components.

The next component will then pack our 4-bit values into a 32-bit word. It will also keep track of word transitions, and note when any new word begins. Upon the beginning of a word, or for that matter any unknown character, the interface will push forward any command currently within its buffer.

hbpack	packxi(i_clk, w_reset,
	dec_stb, dec_bits, iw_stb, iw_word);

You may have noticed by now that I like to differentiate things by prefix. The ouputs of this module have the iw_ prefix, indicating that they are the incoming words.

Had you tried to jump into this whole process at the beginning, without the experience of having done all of our work so far, you might’ve thought that the bus master component would be the primary component. The reality is that thus bus master function wouldn’t be able to function apart from the other components around it. The reason is that something needs to tell it what addresses to read and write from the bus.

hbexec	wbexec(i_clk, w_reset, iw_stb, iw_word, wb_busy,
		ow_stb, ow_word,
		o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data,
			o_wb_sel, i_wb_ack, i_wb_stall, i_wb_err,
			i_wb_data);

Here, it takes its inputs from the iw_word, initiates a bus command, and returns its results in the ow_word. Both of these words will be true on the one clock with their _stb line is true. The actual bus master wires are prefixed with o_wb_ if they go from the master to the slave, and i_wb_ if they are returning values from the slave. Further, because these have the i_ and o_ prefixes, you can tell (from my own notation convention) that these particular values are module level inputs and outputs.

At this point, our interface changes somewhat. Prior to this part of our design, subsequent modules were required to be prepared for an input that could take place at any clock. The serial port transmitter, however, at the other end of this stream cannot handle data at any rate given to it. So, we’re going to need to slow things down. We’ll do this with the _busy signal. Each unit in this return chain will accept a _stb word indicating that it’s next input word is valid and ready, and it will produce a _stb word indicating that it’s output word is valid and ready. However, to keep the interface from moving faster than our resulting transmit transport can handle, we’ll insist that transactions only take place when the _stb is valid and the _busy associated with that stage is false. Hence, once the _stb signal is raised, it must remain so and the _word value must not change until the _busy line from the receiving entity has dropped.

This is actually a fairly common protocol, used by busses ranging from AXI to Wishbone and more.

The first component to use this new interface approach is the addints module that adds interrupt notifications into our stream.

hbints	addints(i_clk, w_reset, i_interrupt,
		ow_stb,  ow_word,  int_busy,
		int_stb, int_word, idl_busy);

This stage also acts like a one-stage FIFO, giving us a little bit of a buffer should responses come from the master too fast for us to deal with them.

You can see the _busy line coming from the next stage on the third line, together with the output lines int_stb and int_word. The first line contains the int_busy line which would’ve gone to the prior stage, had we had one that accepted it. We’ll leave it here unconnected so that we can grab it when we add a FIFO in to this interface later.

The hbints module is followed by the debug idles component that adds idle words into the output stream.

	hbidle	addidles(i_clk, w_reset,
			int_stb, int_word, idl_busy,
			idl_stb, idl_word, hb_busy);

We then unpack our words back into the five-bit components that had composed them originally on input.

hbdeword unpackx(i_clk, w_reset,
		idl_stb, idl_word, hb_busy,
		hb_stb, hb_bits, hx_busy);

These 5-bit components then get converted into bytes for the transport.

	hbgenhex genhex(i_clk, hb_stb, hb_bits, hx_busy,
			hx_stb, hx_byte, nl_busy);

If the transport ever clears and becomes idle, we’ll add a carriage return linefeed pair to the interface.

hbnewline addnl(i_clk, w_reset, hx_stb, hx_byte, nl_busy,
		o_tx_stb, o_tx_byte, i_tx_busy);

That way any line-based stream logic will know to flush its buffer, since any partial response would’ve now been completed.

Future Posts

Although this concludes the Verilog development of the debug bus we’ve been working through, the task isn’t over. If you have a wishbone bus that you would like to experiment with commanding, feel free to fire this up and give it a try. At this point, you have the basics of a capability.

The basics. In otherwords, while it’s an RTL bus, it doesn’t do anything yet. We can use it to gain access into a system, but we don’t (yet) have any system built that we might use this to gain access to. So, we’re not done yet.

The next items in this series will discuss:

  • How to create a simple wishbone interconnect

    This will consist of some “interesting” things that we can put together and test. We’ll use a block RAM component and a Wishbone scope for this purpose, as well as a fairly generic set of simple registers.

  • Creating a hand-controlled test bench to prove this works

    This will be about building a test bench that we can use to run this whole package in Verilator.

    Once you get this far, you have an example of something that you can put in your hardware and try. But … we’re still not done. While you may find the interface somewhat usable, a software controller would make it much easier to use.

  • Creating a software bus controller

    That hand-typed interface is going to get old. We’ll build a software interface when we get to this point.

Still, this simple debugging bus is now our first design component, and almost a first design.