If you’ve never written bus logic before, the wishbone bus is a good place to start. Although other busses have more features to them, the wishbone bus can be simplified into an extremely easy bus to work with.

For our purpose, we’ll be using the B4 version of the specification, and specifically the pipelined mode within it.

Wishbone bus components

The first step, though, is to simplify the wishbone bus for our discussion. As with the other logic I have presented, I prefix ports with i_ if they are inputs, and o_ if they are outputs. Further, because these inputs and outputs to our bus slave are wishbone connected, I’ll adjust their prefixes to read i_wb_ for inputs and o_wb_ for outputs.

Using this notation, the wishbone specification defines the following wires from the perspective of a slave:

  • i_wb_cyc is true any time a wishbone transaction is taking place. It needs to become true on (or before) the first i_wb_stb clock, and generally stays true until the last o_wb_ack.

    We’re going to assume that the i_wb_cyc line is high during our transaction, and that the logic necessary to insure this has already been taken care of within the bus master. Therefore, we’ll ignore this wire.

    Incidentally, the specification isn’t clear regarding whether or not this signal can be ignored by the slave. (It cannot be ignored by the interconnect …) If you are dealing with a master you aren’t certain of, you can and your i_wb_stb line with your i_wb_cyc signal and do just as well.

  • i_wb_stb is true for any bus transaction request. While i_wb_stb is true, the other wishbone slave inputs (i_wb_we, i_wb_addr, i_wb_data, and i_wb_sel) are valid and reference the same transaction. The transaction is accepted by the slave any time i_wb_stb is true at the same time as o_wb_stall is false.

  • i_wb_we is true for any write requests

  • i_wb_addr contains the address of the request

    From the perspective of the wishbone slave that we are working with, this address contains only the address lines of relevance to the slave. Hence, while the bus may have 32 address lines, the slave might only be interested in might have anywhere between no address lines and all 32 address lines.

  • i_wb_data contains the data we wish to write.

  • o_wb_ack is a response from the slave, indicating that the request has been completed. For every request given to the slave, there must be one and only one clock cycle with o_wb_ack high

  • o_wb_stall is used to control the flow of data into the slave. It will be true on any cycle when the master cannot accept data from the slave, and false any time data can be accepted. This allows the slave to control the flow of requests being given to it.

    For our simple example, we’ll just set this line to zero.

  • o_wb_data is the data returned by the slave to the bus master as a result of any read request. It is valid whenever o_wb_ack is true, and it’s value may be ignored any time o_wb_ack is false.

While the spec, defines other wires that may be a part of this interface, we’ll ignore these and instead focus on the minimum required logic necessary to get a simplified demonstration up and running.

Writes to the slave

You may remember from our discussion on how to write to a very simple bus that we based all of our logic off of an i_wr line. Whenever this line was true, we would read from the bus. As a review, the simple bus write logic was written as,

always @(posedge i_clk)
	if (i_wr)
		memory[i_addr] <= i_data;

Moving from this simple example to the wishbone is quite easy. In particular, all we need to do is to adjust the i_wr logic for the wishbone bus. Once done, the interaction should look identical:

always @(posedge i_clk)
	if ((i_wb_stb)&&(i_wb_we)&&(!o_wb_stall))
	begin
		// Your write logic here, such as
		// memory[i_addr] <= i_data;
	end

Simple, huh?

Reads from the slave

Reading from the port is fairly easy as well. The first step is to create a response for every address given to us, as we did before.

always @(posedge i_clk)
	//
	// Basically o_wb_data <= memory[i_wb_addr];
	//
	// If you aren't implementing a memory, your
	// result will instead look like ...
	case(i_wb_addr)
	4'h0: o_wb_data <= some_register;
	// ...
	default: o_wb_data <= some_default response;
	end

One more step is required for both reading and writing: we need to return an acknowledgement back to the bus that this transaction has been completed, and that the data on the bus is now valid.

always @(posedge i_clk)
	o_wb_ack <= ((i_wb_stb)&&(!o_wb_stall));

For some peripherals, the answer is known before you request it. In that case, you might just assign the o_wb_ack signal instead of waiting for the positive edge of the clock.

Other peripherals may need to delay the acknowledgement by another clock. Two examples of this would be the wishbone scope and the wbuart serial port controller. Both of these examples require one clock to calculate an output, and then another clock to select between possible outputs.

The other key piece to any wishbone interaction is the stall line. For our simple interaction, we’ll just hold this line zero.

assign	o_wb_stall = 1'b0;

What that means is that we are able to accept a wishbone request on every clock cycle, and that our transaction rate will be as fast as the clock rate. This works well for many peripherals, although some peripherals such as flash or SDRAM may need to adjust this line if they cannot accept a new request every clock cycle.

What if you can only accept requests … sometimes?

So … what if your peripheral cannot accept requests on every clock cycle?

Here’s an example set of logic where the peripheral starts a state machine upon any request, and only returns a result once the state machine becomes idle.

reg	busy;

always @(posedge i_clk)
	if ((i_wb_stb)&&(!o_wb_stall))
	begin
		state <= SOME_NEW_STATE;
		local_data <= i_wb_data;
		busy <= 1'b1;
	end else case(state)
	SOME_NEW_STATE: begin
		// Your logic here ...
		if (some_condition)
			state <= SOME_NEXT_STATE;
		end
	...
	SOME_FINAL_STATE: begin
		state <= IDLE_STATE;
		busy <= 1'b0;
		ack  <= 1'b1;
		o_data <= any_read_data_response;
	enddcase

assign	o_wb_stall <= busy;

Examples of this in practice include the wishbone ICAPE2 controller, and a QSPI flash controller.

What can you use this interface for?

What can you use this for? Anything! Feel free to look over our projects page for some example peripheral components that use this simple interaction.

In particular, I intend to follow up this article with the idea of using a wishbone interface to create an interface whereby a scope may be read for improved debugging purposes.