In a previous post, I discussed how to build a simple wishbone bus slave. That post eliminated a lot of the bus lines so as to make building a simple bus slave easy.

As a result, that bus as built could not handle 8-bit bytes.

This post will describe how to add the appearance of 8-bit addressing to a wishbone bus slave.

Bus Word Size

The word size of any bus is given by by the number of data lines contained within that bus. The speed of the bus is constrained to be one transaction per clock, at most, and hence any bus design can at most read (or write) one bus word at a time. Hence, increasing the width of the bus will increase the throughput of that bus.

The consequence of this, though, is that addresses on the bus don’t reference octets (8-bit bytes), but rather words. Instead, a wider bus represents an array of words, not an array of bytes.

The problem with word references is that most software is built with the legacy understanding that memory exists as a series of octets (8-bit bytes).

So … although the bus exists as an array of words, modern computer software considers it to be an array of octets.

How shall this illusion be kept?

Select lines

The answer to the question of how to make an array of words look like an array of bytes is the bus select lines.

Remember how we defined how a memory would access a bus earlier?

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

Or how we discussed what this would look like for the wishbone bus?

always @(posedge i_clk)
	if ((i_wb_stb)&&(i_wb_we))
		memory[i_addr] <= i_data;

Adding select lines to this transaction is fairly easy. In particular, the four select lines indicate which byte on the bus has valid data within it. What we do, therefore, is to gate each of the individual byte writes by this select line:

	reg	[(BUS_WIDTH-1):0]	mem	[(0:((1<<(ADDRESS_WIDTH)-1)];
	always @(posedge i_clk)
	begin
		if ((i_wb_stb)&&(i_wb_we)&&(w_sel[3]))
			mem[i_wb_addr][31:24] <= i_wb_data[31:24];
		if ((i_wb_stb)&&(i_wb_we)&&(w_sel[2]))
			mem[i_wb_addr][23:16] <= i_wb_data[23:16];
		if ((i_wb_stb)&&(i_wb_we)&&(w_sel[1]))
			mem[i_wb_addr][15: 8] <= i_wb_data[15:8];
		if ((i_wb_stb)&&(i_wb_we)&&(w_sel[0]))
			mem[i_wb_addr][ 7: 0] <= i_wb_data[7:0];
	end

Format here is important. Just a subtle change in this formula, and the synthesis tool may no longer infer a block RAM. If this happens, your LUT usage is likely to massively increase. To deal with this issue, know where to find the guide that specifies how block RAM’s are inferred. For example, here’s a link to the guide for Xilinx.

If you can’t get your synthesis tool to recognize a memory with select lines, you may have to do this final bus decoding yourself:

reg	[(7-1):0]	mem_a	[(0:((1<<(ADDRESS_WIDTH)-1)];
reg	[(7-1):0]	mem_b	[(0:((1<<(ADDRESS_WIDTH)-1)];
reg	[(7-1):0]	mem_c	[(0:((1<<(ADDRESS_WIDTH)-1)];
reg	[(7-1):0]	mem_d	[(0:((1<<(ADDRESS_WIDTH)-1)];

always @(posedge i_clk)
begin
	if ((i_wb_stb)&&(i_wb_we)&&(w_sel[3]))
		mem_a[i_wb_addr] <= i_wb_data[31:24];
	if ((i_wb_stb)&&(i_wb_we)&&(w_sel[2]))
		mem_b[i_wb_addr] <= i_wb_data[23:16];
	if ((i_wb_stb)&&(i_wb_we)&&(w_sel[1]))
		mem_c[i_wb_addr] <= i_wb_data[15:8];
	if ((i_wb_stb)&&(i_wb_we)&&(w_sel[0]))
		mem_d[i_wb_addr] <= i_wb_data[7:0];
end

If you have to do this decoding yourself, reading from the bus would also change to:

always @(posedge i_clk)
	o_wb_data <= { mem_a[i_wb_addr], mem_b[i_wb_addr],
		mem_c[i_wb_addr], mem_d[i_wb_addr] };

But I tend to avoid this approach if at all possible–it just seems and feels messier.

What about reading from the bus?

The only time you need to worry about the select lines when reading from a wishbone bus is when you are the master and you want to decode the result from the bus. Hence, the bus master will just read a whole word, and then grab the byte (or bytes) necessary when the bus request returns its data.

In other words, nothing changes in the slave when you read less than a word size from the bus.

Do all peripherals need to offers 8-bit support?

Not at all. If you read through device specification sheets, it’s not uncommon for the designer to say that accesses of less than a word size are not supported, and that their results are undefined. This is the hardware designer’s way of saying that the select lines may not be relied upon.

In other words: when building your peripheral, you don’t need to provide select line support.

However, in order for the various string libraries to work, memory peripherals must have select line support.

Examples

Few of my generic peripherals support memory byte selection, but all of my memory peripherals now do. You can find an example block RAM memory device here, showing all the required pieces of what it takes to interact with a bus slave.

If this is interesting to you, then stick around: discussing how to build a wishbone bus master is next!