Last time we got the simplest of digital circuits working: an LED flasher. Now its time to move on to something more interesting. We're going to design a serial communication module so that our hardware can communicate with other devices.

Communication instantly adds an element of interactivity. We'll be able to send commands to the hardware and/or receive information back (like switch statuses). But let's not get ahead of ourselves. Right now the goal is to simply be able to send and receive data.

UART: What is it?

UART is an acronym for Universal Asynchronous Receiver Transmitter. That's quite a mouthful, so let's stick with UART. It's a serial data transmission standard that can transmit bits of data using only one wire (per direction). Okay, it's actually two wires if you include the ground wire, and a minimum of three wires to achieve bidirectional communication (transmit, receive, and ground). 

The "asynchronous" part means that there is no clock signal. This removes the need for a clock wire, but also means that both the sender and the receiver need to be using the same clock frequency.

UARTs have been around for a long time, and are relatively slow; a common speed is 115,200 baud (baud = bits per second including the data framing bits). What's important for us is:

  • The UART protocol is pretty easy to implement
  • The Papilio Pro's USB connection has a UART built in that we can use

If you're not using a Papilio Pro, then fear not because UARTs are very common. Many development boards have them built in, including microcontroller boards such as the Arduino. It's also very easy to get USB to serial adaptors.

WARNING: USB to serial adaptors typically use the RS-232 standard, which uses signalling voltages that can damage your FPGA if connected directly. Either use an RS-232 to 3.3V level shifter, or use an adaptor that specifically uses TTL/3.3V signalling.

The Data Frame

The UART protocol encloses data bits in a frame. There are three parts to this frame:

  • A start bit - The start bit lets the receiver know that data is about to be transmitted (a low/0 is sent)
  • The data payload - This is the actual data, and may also contain a parity error-checking bit
  • A stop bit (or bits) - Marks the end of the frame (a high/1 is sent)

When nothing is being transmitted, then the serial line is held high, which matches the stop bit. When plotted versus time, a single data frame looks as follows:

Serial frame

There are various options in the UART protocol for stop bits, parity, etc., but lets keep it simple. We will make a UART that runs at 115.2 kb/s and sends 8-bits per frame (i.e., a byte), with no parity and one stop bit. This is commonly abbreviated to 115200/8N1.

Step 1: New Project

In ngDesign create a new project called SimpleUART (File => New Project). Next, create a new package (right-click on SimpeUART/src in the project explorer, and select New => Package). Call it something like "com.keasigmadelta.simpleuart." Replace "com.keasigmadelta" with your company/organisation/personal details as per Java style naming conventions.

Step 2: Constants

There are several constants that are needed to keep all parts of the UART running at the correct speed. So, create a new bundle in com.keasigmadelta.simpleuart called UartConsts (right-click on com.keasigmadelta.simpleuart, and select New => Cx bundle). A Cx bundle is the equivalent of a header file in C/C++. It's a useful place to put constants and functions that will be used in multiple places.

Enter the following bundle code:

bundle UartConsts {
	typedef u9 ClkCounter_t;
	
	u32 CLOCK_FREQ = 32000000;    // clock in Hz
	u32 BAUD_RATE = 115200;
	u8 OVERSAMPLING = 16;
	u8 SAMPLE_THRESHOLD = OVERSAMPLING / 4 - 1;

	ClkCounter_t TX_DIVISOR = CLOCK_FREQ / BAUD_RATE;
	ClkCounter_t RX_DIVISOR = CLOCK_FREQ / (OVERSAMPLING * BAUD_RATE);
}

Here's what the constants are:

  • CLOCK_FREQUENCY - the input clock frequency; 32 MHz, in the Papilio Pro's case
  • BAUD_RATE - the number of bits sent per second, including the start and stop bits
  • OVERSAMPLING - how many times per serial clock the receiver will sample the input (I'll explain why we need this later)
  • SAMPLE_THRESHOLD - how many samples to read before deciding if the input is a 1 or 0
  • TX_DIVISOR - the clock frequency divider needed to get the transmit clock
  • RX_DIVISOR - the frequency divider needed to get the receiver's sample clock

Step 3: The Transmitter

Let's design the transmitter first. The transmitter takes in a byte, and converts it into a serial stream of bits. A byte is an 8-bit number and can be used to store small numbers or a single charater (e.g., an 'A' is encoded as the value 65 in ASCII). See this page for more about what a byte is.

Back to the task at hand. Here's the UART transmitter's basic algorithm:

  • Wait until there's a byte to send
  • Output the start bit (which is a 0)
  • Wait one serial clock cycle
  • For each bit in the byte, starting with the Least Significant Bit (LSB):
    • Output the bit
    • Wait one serial clock cycle
  • Output the stop bit (which is a 1)
  • Wait one more serial clock cycle
  • Go back to the start, and repeat

The LSB is the bit in the byte that has the smallest value. Makes sense? No? Okay, let's look at the decimal equivalent. For the number 102 the Least Significant Digit (LSD) is the 2 because it has the lowest value. Similarly, the Most Significant Digit (MSD) is 1, because its value is 100. With binary numbers, each digit can only be a 1 or a 0, and that's called a bit. So LSD becomes LSB, and MSD becomes MSB (Most Significant Bit).

Okay, that's the theory out of the way. Now lets write the actual code. Create a new task called UartTx (right-click on com.keasigmadelta.simpleuart, and select New => Synflow task). Enter the following code:

package com.keasigmadelta.simpleuart;

import com.keasigmadelta.simpleuart.UartConsts.*;

task UartTx {
	/** The input port.
	 */
	in sync ready u8 din;
	
	/** The serial output
	 */
	out bool dout;
	
	u8 data;
	u4 i;
	
	void setup() {
		// UART signal is high when there's nothing
		dout.write(true);
	}
	
	
	void loop() {
		data = din.read();
		
		// Send the start bit
		StartBit:
		dout.write(0);
		waitUARTClock();
		
		// Send the bits
		SendData:
		for(i = 0; i < 8; i++) {
			SendBit:
			dout.write(data[0]);
			data >>= 1;
			waitUARTClock();
		}
		
		// Send the stop bit
		StopBit:
		dout.write(true);
		waitUARTClock();
	}
	
	ClkCounter_t count;
	
	/** Waits one UART clock cycle
	 */
	void waitUARTClock() {
		for(count = (ClkCounter_t)(TX_DIVISOR - 1); count != 0; count--) {
			// Doing nothing
		}
	}
}

Let's take a look at a few key items in the code above. First, notice that the din input has the sync and ready flags. Sync adds a "data available" signal and ready adds a "ready for new data" signal. The transmitter needs these so that data is

What does the sync flag mean in practise? It means that the following line will wait until there's data to read before continuing:

data = din.read();

Likewise, likewise, the ready flag means that external tasks writing to din will wait until UartTx is ready to receive another byte before continuing. See the Cx port documentation for more information about port synchronization.

The next item of interest is the waitUARTClock() function. Functions can be used to encapsulate code that will be used in several places. In this case there are several places where we need to wait for one serial clock cycle, so we put that in waitUARTClock().

Next, take a look at the following line:

data >>= 1;

If you're familiar with C or C++ then you'll recognize this as a right-shift operation. Basically, it takes the bits in data, and shifts them all to the right by one bit. The LSB is dropped, while a 0 is shifted in to the MSB. In this manner the bits are sent to dout one by one.

Finally, the loop() contains several state labels:

  • StartBit: sends the start bit
  • SendData: the loop that sends the data bits
  • SendBit: sends an individual data bit (this is inside the SendData loop)
  • StopBit: sends the stop bit
  • Ready: signals that the transmitter is ready to send the next byte

These labels are there for understandability, and have no effect on the actual hardware. If you look at the Finite State Machine (FSM) diagram in the right-hand column of ngDesign, then you'll see the state names appearing in the relevant states (see below). Hovering over the states (circles) or transitions (lines with arrows) will show you the code associated with each state/transition.

UartTX FSM

You should look through the setup() and loop() functions, and make sure you understand what each line does. Setup() sets the initial state (ready to send, and the output line is in the default high state). The loop() function transmits bytes one by one as they come in. Use the state labels as a guide, and compare them to the UART graph above.

HINT: Hover the mouse over the states & transitions in the FSM window (as described above) to see the code per state & transition.

Step 4: The Receiver

Receiving is more complicated than sending. The transmitter simply has to spit out the bits at the right speed, whereas the receiver has to interpret the data stream and reconstruct the original data. To complicate things, noise on the line could result in faulty bits being received if we're not careful.

The solution to the noise problem is to sample the incoming data several times per serial clock cycle, and filter the result. To achieve this we're going to divide the receiver into three parts: a prescaler, filter, and the actual receiver. These are connected together as per the block diagram below.

UartRx Block Diagram

Start by creating a new network called UartRx (right-click on com.keasigmadelta.simpleuart, and select New => Synflow network). We're going to create the sub-components directly inside UartRx, because they're relatively small and specific to the UART receiver. Hence, there's no need to create separate files for each.

UartRx has one input and one output:

/** The serial input
 */
in bool din;
	
/** The output
 */
out sync u8 dout;

Notice that dout is also sync'd. That's because any hardware that receives data via this UART is going to need to know when a new byte has arrived for it to read.

Prescaler

Prescaler is another name for a clock divider; it takes in one clock, and produces an output tick at a lower frequency. The term prescaler seems to be commonly used in UARTs, so let's stick with that.

We're going to need access to the constants that we defined earlier, so add the following line to the top of the file:

import com.keasigmadelta.simpleuart.UartConsts.*;

Next, add the prescaler code below the UartRx's input & output port code:

/** The prescaler generates the receiver's sampling clock
 */
prescaler = new task {
	out bool tick;
	
	ClkCounter_t count;
	
	void loop() {
		// Wait RX_DIVISOR clock cycles
		tick.write(0);
		for(count = (ClkCounter_t)(RX_DIVISOR - 1); count != 0; count--) {}
		
		// Emit tick for one clock cycle
		tick.write(1);
	}
};

This code sets the tick output to 1 once every RX_DIVISOR clock cycles. If you look back at UartConsts, then you'll see that this produces a tick at OVERSAMPLING times the serial clock (i.e., 1.8432 MHz). This is the rate at which the filter samples the input.

Filter

As the name suggests, the filter's task is to remove noise from the input. While digital signals are fairly robust to noise, long cables can have voltage spikes induced in them, resulting in a 0 being read instead of a 1 and vice versa. This is particularly true in environments with power hungry machinery, like factories.

Anyway, we're going to filter noise spikes out by sampling the input multiple times per serial clock, and use thresholds to decide when the value flips between 0 and 1 (or vice versa). The algoriithm is:

  • If din is high (i.e., 1) and the counter doesn't equal SAMPLE_THRESHOLD, then increment the counter by one
  • Else if din is low (i.e., 0) and the counter doesn't equal 0, then decrement the counter by one
  • Else if counter equals SAMPLE_THRESHOLD, then write a 1 to dout
  • Else if counter equals 0, then write a 0 to dout
  • Otherwise, do nothing
  • Repeat the above every sample clock tick

This algorithm is implemented in Cx as follows:

/** Filters the input stream to eliminate noise, and provide stable data.
 */
rxFilter = new task {
	/** Serial data in
	 */
	in bool din;
	
	/** Clock tick in
	 */
	in bool tick;
	
	/** Filtered data out
	 */
	out bool dout;
	
	u2 counter;
	
	void setup() {
		// Need to start high (default state for no data)
		counter = (u2)SAMPLE_THRESHOLD;
		dout.write(1);
	}
	
	void loop() {
		if (tick) {
			// Increment/decrement the counter
			if(din.read && counter != SAMPLE_THRESHOLD) {
				// Received a 1, increment the counter
				counter++;
			} else if(!din.read && counter != 0) {
				// Received a 0, decrement the counter
				counter--;
			} else if(counter == SAMPLE_THRESHOLD) {
				// Reached threshold for 1
				dout.write(1);
			} else if (counter == 0) {
				// Reached threshold for 0
				dout.write(0);
			}
		}
	}
};

Compare the code to the algorithm, and make sure that you can match each line in the algorithm to the corresponding Cx code. Also, notice the if (tick) { ... } that encloses everything else in the loop. This ensures that the filter operates once every tick, so it's filtering at the correct rate.

The filter needs to be connected to its inputs, which is achieved with the following code (put directly below the rxFilter code):

// Connect the filter inputs
rxFilter.reads(din, prescaler.tick);

This connects the din input to UartRx's own din, and the tick input to the prescaler's tick output. The order that these inputs are connected correspond to the order in which they were defined in the task (compare the line above to the rxFilter code).

Receiver

The filter is now delivering stable serial input data. Now it's time to actually read it, and convert each data frame back to a byte. The receiver needs to:

  • Wait for the start bit (i.e., wait for the serial input to go to 0)
  • Wait for the first data bit
  • Shift the data bits into a register (variable) one-by-one
    NOTE: A register is hardware used to store a variable
  • If the stop bit is detected, then
    • Output the data (we have a valid data frame)
    • Wait for the stop-bit to end
  • Else, we have a data framing error, and the data is discarded
  • Go back to the start

Translating this to Cx code, this becomes:

/** The actual receiver. 
 * Reads the serial stream, and converts it back to bytes.
 */
receiver = new task {
	/** The (filtered) input data.
	 */
	in bool din;
	
	/** Clock tick in
	 */
	in bool tick;
	
	/** The output data
	 */
	out sync u8 dout;
	
	/** Internal data register
	 */
	u8 data;
	
	/** Data bit counter
	 */
	u4 dataCount;
        
        void setup() {
            // Wait for the first tick (avoids receive attempts during setup)
            while(!tick.read) {}
        } void loop() { // Wait for the start bit WaitStart: while(din.read == true) { // Wait } // Read the start bit // NOTE: Waiting one sample threshold period longer, so that we sample the // filtered serial signal when it's stable Start: for(clkCount = 0; clkCount != (OVERSAMPLING + SAMPLE_THRESHOLD); clkCount++) { // Wait one tick while(!tick.read) {} } // Now clock in the data Data: for(dataCount = 0; dataCount != 8; dataCount++) { ReadDataBit: // Shift the existing bits over data >>= 1; // Read the new bit data[7] = din.read; // Wait for the next bit WaitSerialClock: waitSerialClock(); } // Read the stop bit Stop: if(din.read == true) { // Yes, have stop bit, output data dout.write(data); } } ClkCounter_t clkCount; /** Waits one serial clock cycle. */ void waitSerialClock() { for(clkCount = 0; clkCount != OVERSAMPLING; clkCount++) { // Wait one tick while(!tick.read) {} } } };

NOTE: The setup() function is simply a delay; we don't want the receiver reacting while the hardware is being initialised.

As with rxFilter, the receiver task needs to be connected to its inputs and outputs:

// Connect the receiver's inputs and outputs
receiver.reads(rxFilter.dout, prescaler.tick);
this.reads(receiver.dout);

By now you should be able to read through the code and recognize the algorithm as described above. I would like to call your attention to two items, though. First, note that it waits 1.25 serial clock cycles from the beginning of the start bit (the for loop waits (OVERSAMPLING + SAMPLE_THRESHOLD) cycles). This may look odd, but it ensures that we sample the data bits when they're stable. If we sampled them right at the starting edge of each bit then timing jitters and noise would corrupt the data. I actually tried this while creating this tutorial, and the receiver failed miserably.

Next, take a look at this code snippet:

ReadDataBit:
// Shift the existing bits over
data >>= 1;
			
// Read the new bit
data[7] = din.read;

This is the code that shifts the serial data into the data register LSB first, and converts it to a byte. As with the transmitter, the >>= operator shifts the bits to the right. What's different this time is that a new bit is read from din into data's MSB (data[7]). This code implements a shift-register that shifts in a bit from an external source (which is the din serial input, in this case).

Step 5: Verifying the Design in Simulation

I'm sure that you're wondering if all of this code is going to work. While we could try it on real hardware, a much better idea is to run it in a simulator first. Simulators are very useful even though FPGA's allow us to test designs at low cost. They allow you to:

  • Write and run tests that test both whole designs and individual units, and provide a pass/fail output
  • Analyse the behaviour of every single signal in the design, which is essential to debugging faulty hardware

For now we'll create one test to confirm that both the transmitter and receiver work. To achieve this, we're going to set up a test bench. A stimulus module will send bytes to the UART transmitter, which will send them through to the receiver. Finally, a monitor will read the bytes from the receiver, and check that they match the bytes that the were sent. The test bench looks as follows:

UartTest

Create a new network called UartTest. We're going to need some test data that both the stimulus and monitor can access. So, create the following bundle in UartTest.cx above the network itself (i.e., above "network UartTest {"):

bundle UartTestData {
	const u8 length = 22;
	const char testData[length] = "Testing, testing, 123.";
}

You're welcome to replace the test data with anything you like. The design should be able to handle any data set; otherwise it would be faulty.

Next, create the stimulus task inside the UartTest network:

/** Generates our sample data.
 */
stimulus = new task {
	
	out sync ready u8 dout;
	
	void setup() {
		// NOTE: Doing this in setup, because we only want to send the test data once
		for(u8 i = 0; i != UartTestData.length; i++) {
			dout.write(UartTestData.testData[i]);
			print("Sent: ", UartTestData.testData[i]);
		}
	}
};

This task outputs the bytes one by one, and also prints a copy to the simulator's console window (see the print statement in the code above).

Next, add the UART transmitter and receiver, and connect them up:

/** The Uart transmitter and receiver, wired up to loop-back
 */
uartTx = new UartTx();
uartTx.reads(stimulus.dout);

uartRx = new UartRx();
uartRx.reads(uartTx.dout);

Finally, create the monitor task, and connect it to uartRx's output:

/** Check that the data matches
 */
monitor = new task {
	in sync u8 din;
	
	void setup() {
		for(u8 i = 0; i != sizeof(UartTestData.testData); i++) {
			u8 data = din.read;
			print("Received:", data);
			assert(data == UartTestData.testData[i]);
		}
	}
};
monitor.reads(uartRx.dout);

The monitor prints the received data, and also checks if it matches what was originally sent. The check is performed using the assert function:

assert(data == UartTestData.testData[i]);

Assert basically means "I assert that the following will always be true." So, if data does not equal UartTestData.testData[i], then assert() will throw an exception and you'll get a big red error message in the console window.

Right, let's perform the test! Right-click on UartTest.cx (in ngDesign's left column), and select Run As => Simulation. If you entered all the code correctly, then you'll get the following output:

Simulation started
Sent: 0x54
Received:0x54
Sent: 0x65
Received:0x65
Sent: 0x73
Received:0x73
Sent: 0x74
Received:0x74
Sent: 0x69
Received:0x69
Sent: 0x6e
Received:0x6e
Sent: 0x67
Received:0x67
Sent: 0x2c
Received:0x2c
Sent: 0x20
Received:0x20
Sent: 0x74
Received:0x74
Sent: 0x65
Received:0x65
Sent: 0x73
Received:0x73
Sent: 0x74
Received:0x74
Sent: 0x69
Received:0x69
Sent: 0x6e
Received:0x6e
Sent: 0x67
Received:0x67
Sent: 0x2c
Received:0x2c
Sent: 0x20
Received:0x20
Sent: 0x31
Received:0x31
Sent: 0x32
Received:0x32
Sent: 0x33
Received:0x33
Sent: 0x2e
Received:0x2e

Success! It works.

NOTE: This isn't a fool-proof test. It tests whether the UartTx and UartRx can communicate with each other, but not whether they meet the UART specification. Nor does it test resilience to random data spikes, etc. We'd need other tests to check those as well.

Final Words

We have created a UART transmitter and receiver, and confirmed that they work via simulation. I think that's enough for today. Implementing it in hardware will be done next time. Besides, the current design has a flaw in it that could be a problem if we try to use it to communicate with other hardware. Do you know what it is? I'll explain next time.