This is our next post in how to build a wishbone controlled debugging port into your design. Other posts on this topic include:

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

These two articles have presented the overview shown in Fig. 1 as a simple bus overview that we might use. Using this overview as a guide, we’ve discussed how to build the bus master. We’ve said nothing (yet) about FIFO’s, resets, interrupts, or converting response words back to bytes. Today, though, we’re going to discuss how to create the command words from the incoming interface. We’ll also use this incoming port to generate a reset for the rest of the port.

We’ll do this in two parts. The first part will turn our 7-bit incoming printable ASCII bytes into a shorter, simpler, 5’bit values formatted to be used by the next component. The second part will actually pack these bits into words and send them to our bus master (since we have no FIFO yet …).

Design Outline

We’d like to turn printable ASCII into command words. We’re going to design this ASCII in such a fashion that every command word will begin with a capitol letter identifying the command, and end with 0-8 nibbles of hexadecimal characters. So, let’s look at what that requires:

  • Addresses will begin with an “A” character.

  • Read requests will consist of an “R” character alone.

  • Reset requests consist of a “T” character.

  • Write requests will start with a “W” character.

  • Write requests and address values will be followed by 0-8 nibbles of value. This value will encode either the value to be written, or the new address to be set.

  • Unspecified command word bits will be set to zero, allowing unsigned extensions of words.

  • Addresses will end in multiples of four. If the 2 bit is set on an address, the value given will be added to the last address register. If the 1 bit is clear, the address will be incremented (by 4) on every read or write. Hence, “A2040RRRR” should read four consecutive values starting with the 0x02040 address.

Some examples of this coding would be:

  • Read from address 0x2040.
A2040R
  • Write 0xdeadbeef to the address 0x2044
A2044Wdeadbeef
  • Write a 1 to the next four consecutive addresses
W1W1W1W1

Our encoding will need to turn these sorts of commands into 34-bit command words suitable for the bus master.

Transforming Printable ASCII to Binary Nibbles

As outlined above, we’re going to focus on a hexadecimal encoding format. This is simply for the purpose of keeping the encoding simple enough so that you can either type the value in via a keyboard, or perhaps read and comprehend it when two pieces of software are talking to each other. In both cases, you want the command channel to be comprehensible.

For this purpose, the values ‘0’-‘9’, and ‘a’-‘f’ will be transformed into bit values 5’h0 to 5’hf. Yes, that is a five bit encoding we’re going to use to go to the next step.

Why? If we are using a hexadecimal encoding, why do we need five bits?

We’ll use the fifth bit to tell us some extra information: is this a read request, a write command, a new address or a reset command? These extra options will use that extra bit, and we’ll generate it from specific letters that aren’t part of the hexadecimal encoding.

For example, if the user types a “T”, we’ll use that as an indication that we want to reset our entire debugging bus component.

always @(posedge i_clk)
	o_reset <= (i_stb)&&(i_byte[6:0] == 7'h54);

Otherwise, if we receive anything other than an incoming nothing character 7’h7f, we’ll be producing an output.

always @(posedge i_clk)
	o_stb   <= (i_stb)&&(i_byte[6:0] != 7'h7f);

All that’s left here is to decide what that output value will be.

We’ll handle that with a giant case statement. Although this may look complex, once it gets turned into FPGA logic, this is nothing more than a 7-bit lookup table per output bit, or about 20 LUTs. (Remember how to count LUTs?)

always @(posedge i_clk)
begin
	case(i_byte[6:0])
	// Transform hexadecimal characters '0' to '9' to their
	// binary equivalents, with the out of band flag cleared
	7'h30: o_bits <= 5'h00;
	7'h31: o_bits <= 5'h01;
	7'h32: o_bits <= 5'h02;
	7'h33: o_bits <= 5'h03;
	7'h34: o_bits <= 5'h04;
	7'h35: o_bits <= 5'h05;
	7'h36: o_bits <= 5'h06;
	7'h37: o_bits <= 5'h07;
	7'h38: o_bits <= 5'h08;
	7'h39: o_bits <= 5'h09;
	//
	// Hexadecimal characters 'a' through 'f'
	//	(Note that 'A' is used for 'Address' and hence we don't
	//	support upper case hexadecimal letters here)
	7'h61: o_bits <= 5'h0a;
	7'h62: o_bits <= 5'h0b;
	7'h63: o_bits <= 5'h0c;
	7'h64: o_bits <= 5'h0d;
	7'h65: o_bits <= 5'h0e;
	7'h66: o_bits <= 5'h0f;
	//
	// Other characters set out of band information (o_bits[4])
	// These are primarily the bus command bits
	7'h52: o_bits <= 5'h10;	// 'R', or read command
	7'h57: o_bits <= 5'h11;	// 'W', or write command
	7'h41: o_bits <= 5'h12;	// 'A', set address
	7'h53: o_bits <= 5'h13;	// 'S', "special" ... reserved for later
	7'h54: o_bits <= 5'h16;	// 'T' --set for form only
	default: // an "other" character, to be subsequently ignored.
		// Also used as an end of word character, if received
		o_bits <= 5'h1f;
	endcase
end

But what will we do with this five-bit value result? That’s the next stage.

Transforming Binary Nibbles to Words

To pack four bits at a time into a 34-bit value is going to take several steps.

The first step deals with the upper two bits of the output command word. These are given by the lower two bits in our five bit word, but only when the upper bit is set.

initial	o_word[33:32] = 2'b11;
always @(posedge i_clk)
	if ((i_stb)&&(i_bits[4:2] == 3'b100)) // new command
		o_word[33:32] <= i_bits[1:0];

We may wish to add other “special” command words into our dictionary in the future. Checking for 3’b100 checks that the high bit is set, but also leaves room for us to check for other command words later.

Now, any time we are told what command we are about to do, we’re going to need to keep track of whether or not our register has a command loaded within it. We’ll build our command word up from the first letter indicating what command we are issuing (‘A’, ‘R’, or ‘W’). On any non-hexadecimal character, we’ll flush the command through the system. We’re also going to clear any partial command on any reset request.

initial	cmd_loaded = 1'b0;
always @(posedge i_clk)
	if (i_reset)
		cmd_loaded <= 1'b0;
	else if ((i_stb)&&(i_bits[4:2] == 3'b100))
		cmd_loaded <= 1'b1;
	else if ((i_stb)&&(i_bits[4]))
		cmd_loaded <= 1'b0;

Any thing else, any other special command, will just drop on the flow and the command will go from loaded to unloaded.

The last piece of logic before we build our word itself is the one to determine when to send our word to the next stage. We’ll do that any time we have a command loaded and a new character comes in with that extra high-bit set.

initial	o_stb = 1'b0;
always @(posedge i_clk)
	o_stb <= (!i_reset)&&((i_stb)&&(cmd_loaded)&&(i_bits[4]));

Now, finally, we can build the word that we will output. At this point, building this word in a register is quite easy. On any new value, we just shift the new value into our word and move everything over by four bits.

initial	o_word[31:0] = 0;
always @(posedge i_clk)
	if (i_reset)
		r_word[31:0] <= 0;
	else if (i_stb)
	begin
		if (i_bits[4])
		begin
			// Record the command into our buffer
			r_word[33:32] <= i_bits[1:0];
			// Clear our buffer on any new command
			r_word[31:0] <= 0;
		end else
			// Other wise, new hex digits just get
			// placed in the bottom of our shift register,
			// and everything quietly moves over by one
			r_word[31:0] <= { r_word[27:0], i_bits[3:0] };
	end

Sure, we’ll clear our buffer on any new reset command and any time we get a command word as opposed to a hex digit. That way, if we want to send any short unsigned numbers, we have only to give those unsigned digits. For example, W5W6\n will write the value five to the bus, and then the value 6. With this approach, we don’t need to send all 8-nibbles defining our 32-bit word if we don’t have to.

Finally, for timing purposes, we wait for an incoming value before setting our output word. Why timing? Well, because our output strobe is only true for the immediate clock following the incoming strobe. r_word has also changed by then. Hence, we’ll grab a copy before then of what we wish to send out.

	always @(posedge i_clk)
		if (i_reset)
			o_word <= 0;
		else if (i_stb)
			o_word <= r_word;

That’s it!

That’s all there is to it. You’ve now seen how simple it can be to compose an output word from several input bytes.

Sure, this protocol is simple. It takes us up to 9-bytes to send a 32-bit address, and another 9-bytes any time we wish to write a 32-bit value. In other words, we are sending 72-bits any time we wish to use 34-bits worth of information.

Can this be improved upon? Definitely. But first, let’s just work through what it takes to build our entire interface before we try to improve upon it.