Getting your first communications port up and running on an FPGA can be a real challenge. After you get your serial port up and running, you can then use it to get the next item up and running–since a serial port can provide a lot of feedback. But until you can read that feedback, how can you avoid FPGA hell long enough to get that first port up and running?

The easy way

If you want to do things the easy way, then just connect your serial port logic to a known working UART simulator, such as this one, and examine the VCD output until you get the right answers from the serial port. As an example, consider the helloworld.v module, together with the helloworld.cpp Verilator driver, for inspiration on how you might do this.

Once your hello world design runs in the simulator, and once you get “Hello World” on the screen, you are reading for real hardware.

That’s the easy way.

Building your own simulator

But … what happens if you wish to communicate with your FPGA over something other than a serial port, and you have no serial port to bootstrap your efforts?

In that case, you’ll want to build your own simulator.

Since I’ve done this so often, I maintain a test bench C++ class which I often use for wrapping the Verilator commands within a simulator. While it doesn’t handle all of the Verilator code, it does handle most of it. Still, there’s often quite a bit of work to be done. You can see examples of simulators I’ve built for I2C controller, a PMod MIC controller, a SPI based SD-card controller, or even the wishbone scope, which I hope to work our way up to in this blog.

I would also recommend making heavy use of the C assert statement any time your code must do something. For example, if you must wait at least 20 clocks from any request to the next, then count clocks within your simulator and assert that the time between accesses is less than 20. Just be careful to make certain you flush any VCD file you are creating before calling assert, lest you don’t get the data leading up to it. (The test bench class was recently updated to handle this by default)

Once your code works in your simulated environment, the next step is to try it on the hardware.

The Easy Move to Hardware

The easy way of getting this next step going is to use an oscilloscope. You’ll want to build a development board with particular “test-points” where you can hook up the probe for the scope, and see what’s going on. Barring test points on the actual wires, sometimes you can use a general purpose I/O pin to become a test point.

This is also the expensive way.

I don’t own a scope, neither do I have the cash to design my own boards, so … enjoy this approach if you can do it.

The Harder way to get Hardware Working

The difficult part of this first task on your hardware is, how do you know what’s going on at high speed? If you don’t have an Oscilloscope to measure that high speed interaction, if all you have are your LED’s and your clock, how shall you get to the next step?

The answer is to use your LED(s), just like we discussed when building blinky.

Let’s take a look a some particular tests you can try.

Look for whether or not the serial port is getting set

Your first question is going to be, am I even toggling the right wire, or vice versa, am I listening to the right wire?

In the case of a serial port, the line should idle high (at one) when nothing is being sent. So anytime the line isn’t one is an event you are interested in, right?

always @(posedge i_clk)
	if (!ck_uart)
		counter <= 0;
	else if (!counter[24])
		counter <= counter + 1'b1;
assign	o_led = counter[24];

You did remember to use two flip flops to synchronize an asynchronous input, right?

always @(posedge i_clk)
	r_uart <= i_uart;
always @(posedge i_clk)
	ck_uart <= r_uart;

Put together, this will turn your LED off if the UART line is ever active. Once the character has been sent, the LED should idle high. (This way you know the LED works …) If the LED isn’t idling high, or if the line never goes low, then you know that you may be listening to the wrong wire. If the LED never turns off, you might not have the right serial port coming out of your computer.

Incidentally, this test usually finds most of the UART problems that take place using code that passes the simulation test.

Check to see if your baud rate is set right

Since the start bit in any serial port transaction is always zero, we should be able to check for whether or not that first clock stays low for a full baud duration as follows:

always @(posedge i_clk)
	if ((counter[24])&&(!ck_uart))
	begin
		// If we are idle and something comes across the wire,
		// start counting.
		counter <= 0;
		transitioned <= 1'b0;
	end else if (!counter[24])
	begin
		// While we are not yet idle, count up on the counter
		// to something near a second ...
		counter <= counter + 1'b1;

		// Set the bit_interval counter on *every* clock,
		// up until the UART line goes high again
		if (!transitioned)
			bit_interval <= counter;

		// When that first transition from zero happens, grab
		// how long things had been low.
		if ((ck_uart)&&(!transitioned))
			transitioned <= 1'b1;
	end

// Now that we have a bit_interval estimate, test it and set our LED
always @(posedge i_clk)
	o_led <= ((transitioned)&&&(bit_interval < ONE_AND_A_TENTH_BAUD)
			&&(bit_interval > NINE_TENTHS_OF_A_BAUD));

This should help you discover what speed your port is transmitting at. Just to check, send an “A” to the port. Since “A” is represented by 8’d65, which is an odd number, and since serial ports transmit the LSB first, this should allow you to determine if you are within the baud rate you are expecting. If “A” doesn’t pass, but “B” does, then you’re baud rate is too slow.

This simple test should find most of the rest of your problems.

Check to see if you are discovering any characters at all

At this point, we turn from common and known problems to answering whether we can debug something … not so well known. This example tests whether or not the serial port ever declares that a value has been read (i.e., sets rx_stb high for one cycle).

always @(posedge i_clk)
	if ((counter[24])&&(!ck_uart))
	begin
		counter <= 0;
		found <= 1'b0;
	end else if (!counter[24])
	begin
		if (rx_stb)
			found <= 1'b1;
		counter <= counter + 1'b1;
	end else
		counter[23:0] <= counter[23:0] + 1'b1;

assign	o_led = (found)&&(counter[23]);

Usually, though, if you’ve passed the simulation test, you’ll never have this problem. Still, it can be useful to determine what’s going on.

Did you notice that we kept the bottom 24 bits of this counter counting, even after the 25th bit went high? Or the fact that the LED was the AND of the found light and the 24th counter bit? In other words, if anything was found, the light will blink. If the UART line ever goes active, but rx_stb never goes high during this time frame, then the LED will stay off.

Check to see what character you are reading

If you know you are receiving a character, but don’t know what it is, then … use the LED to find out!

always @(posedge i_clk)
	if ((counter[24])&&(!ck_uart))
	begin
		counter <= 0;
	end else if (!counter[24])
	begin
		if (rx_stb)
			read_data <= rx_data;
		counter <= counter + 1'b1;
	end else
		counter[23:0] <= counter[23:0] + 1'b1;

// If you don't have 8 LED's, there's no reason why you can't do this one
// LED at a time ... it'll just take longer to do.
assign	o_led[7:0] = read_data;

Of course, this solution depends upon your being able to display eight data bits on your LEDs at the same time.

What about the transmitter?

While the examples shown above apply to the UART receiver, there’s no reason why they cannot be turned around and made to work for the transmitter as well. You’ll probably want to check the transmitter output in the reverse order, though: first that it is getting something to send, then whether or not the baud rate is set right, etc.

Possibilities are endless

The number of ways you can set an LED as you work through a problem like this is endless. There are just so many tests you can do.

Even still, … this approach has two problems:

  1. An LED can only give you one bit of information over the course of a quarter second or so … at best! If you need more complete information about what is going on within your design, getting it out via this means will take a long time and may well turn into an exercise in frustration.

  2. LED’s can’t tell you what’s happening on a clock by clock basis. While you might be able to build a serial port without knowing this information, on anything more complex this approach will become a challenge.

So, stick around–how to use an FPGA to debug itself is the subject of this blog.