ASIC from Scratch – Part 3: UART Theory

Veröffentlicht von

Everyone who ever used an Arduino has used a UART, but what exactly is a UART? UART stands for Universal Asynchronous Receiver Transmitter. It’s a simple communication building block that’s fairly simple to implement in hardware. Unlike it’s colleagues I2C or SPI it doesn’t use a clock line. It solely relies on a predetermined baudrate, making the clock kind of implicit. Common baudrates are – among others – 9600 baud (i.e. bit/second), 57600 or 115200. 9600 used to be the Arduino default, 57600 is the new default. 115200 is the highest for a lot of older soft- and hardware. Some more modern microcontrollers like the STM32F103C8T6, which is commonly found on „bluepill“ development boards, can go at up to 2,000,000 baud (and beyond?). In theory you can use any baudrate you want, as long as your partner uses the same. You can go much lower than 9600 or much higher than 115200 (you’ll run into signal integrity problems eventually though).

One nice thing about UART is,that it’s the foundation for many other protocols such as RS-232, RS-485, RS-422 and anything that builds on top of these. UART is the way bits are turned into HIGH and LOW signals, whereas the RS-family defines how HIGH and LOW is turned into electrical signals such as voltages or currents.

For my ASIC I decided to use a low-ish baudrate of 57600. It’s plenty fast for my purposes but still slow enough to easily debug it with my oscilloscope. Having a single fixed baudrate also simplifies filtering a bit, but we’ll get to that later.

First lets start with how UART signals actually look like. Each UART frame consists of a startbit, 8 databits and a stopbit. There are some asterisks attached to that explanation, but this is a good first approximation. The stopbit is a LOW signal before the databits. It is followed by the LSB-first databits, which in turn is followed by the HIGH stopbit. Some software provides the option to chose between one, two or even 1.5 stopbits. 1.5 Stopbits don’t make sense in computer science, but in electronics, where a stopbit is just a HIGH signal, 1.5 stopbits simply means that it is as long as 1.5 times a normal bit. In the end the stop bit is only the pause between two dataframes, so the actual length doesn’t matter a whole lot.

Fig. 1: A simple uart frame consisting of 1 startbit, 8 databits, 2 stopbits.

So far my mental model of ASIC development consists of only 3 building blocks. Of those three I have used two so far: Counters and combinational logic. The third one is probably the most powerful though: The almighty finite state machine, or FSM. (Not to be confused with the flying spagetti monster, which is a different kind of FSM).

For a simple UART only a handful states and transitions are required. So let me explain them using this nice little diagram:

Fig. 2: UART receiver finite state machine.

As you can see, it’s quite manageable. Let’s think about how we would implement the first two states. First we need to detect a falling edge on the uart_rx pin/input. We do that by checking the pin every clock cycle – using the internal high-ish-speed clock of the ASIC. We store the result in a flip flop. Next cycle we check again, if the current state is LOW and the value in the flip flop is HIGH we have our falling edge. Now, a valid start bit would be LOW for the full duration of a bit. To check that we start a counter once we detect a falling edge. There’s a small problem though: If we count to exactly one bit duration, our implementation might run into timing issues if the LSB is 1. To prevent that we simply give it some slack and just consider a LOW signal for 80% of a bit-duration a valid start bit. If uart_rx goes high before that, the counter resets to 0 and we go back into the Idle state where we wait for a falling edge. If it goes high after that, we simply ignore it.

We need to be mindful of our counter values though. After 80% of a bit duration, the line may go high, but we still want to sample the first databit in the middle of where it should be. So 1.5 bit durations after the first falling edge. We could implement that as an additional state, but I think it’s easier to go with a „unclean“ state machine: The first state simply uses a register disable_reset which is set to 1 after the 80% time threshold. If it is high, the reset mechanism is disabled. After the counter reaches 100%, we transition to the next state.

In the next state we read the databits. Remember how we want to sample each bit in the middle? That means we first need to count off 50% of a bit duration. After the 50%, we sample uart_rx. Just like that we have received our first bit! Neato. However, we’re not done yet. First we count the other 50%. At this point we’re at the transition between two bits. Now we increment a „Received Bits“-counter. If that counter ever reaches 8, we transition to the next state.

Lastly we read the stopbit. Since we don’t have any use for it now – and in fact don’t even care whether it is actually HIGH for the full duration, we simply count to 200% of a bit duration. After that we reset the state back to the first one.

Enough theory for now. In the next post I’ll try to implement this in a Verilog module.

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.