UART Receiver

MIT Fall 2024

The questions below are due on Wednesday September 25, 2024; 11:59:00 PM.
 
You are not logged in.

Please Log In for full access to the web site.
Note that this link will take you to an external site (https://shimmer.mit.edu) to authenticate, and then you will be redirected back to this page.

Now that you've written a transmitter for UART, it's time to cover the other side and build a receiver. Unlike the SPI receiver from last week, we don't have a clock to determine when to read our data, since, after all, UART is Asynchronous. So we'll have to be more intentional about when we choose to read each bit we receive.

UART Receiver Sampling

When we want to sample each bit transmitted over UART. Remember, each bit is transmitted for many clock cycles, and we want to sample the bit in the middle of the period we expect it to be transmitted in.

Based on the BAUD rate, we know how long we expect each bit to appear for, but we want to make sure that slight inconsistencies in timing between the transmitter device and receiver device make as little difference as possible. If we measured each bit right as we expect it to first appear, we could unintentionally re-read the previous bit and corrupt our read data--so, instead we want to read each bit received in the middle of the period in which it is meant to be transmitted. This is very, very important.

Just like in our transmitter, we can calculate the UART_BIT_PERIOD we expect for the full period of each bit on the serial wire. Based on that value, we want to read our UART data bits UART_BIT_PERIOD/2 clock cycles into each period.

The way we determine when our count of clock cycles begins is at the beginning of the start (0) bit. The transmission wire can sit idle (logical 1) for an unspecified amount of time, but the moment that it gets pulled low is when we need to start keeping time: the first clock cycle that reads as 0 is determined as the beginning of a data frame, and from there we count our bit periods based on that start time.

Finite State Machine: start and stop bit checking

Once we've begun reading a data frame by detecting a 0 on the receiving wire, by default we expect to see 10 bits appear afterwards, each lasting for UART_BIT_PERIOD cycles: a start bit of 0, the 8 bits of the transmitted data, and an end bit of 1. However, there's a chance that we're actually reading meaningless signals: maybe a loose connection or a digital glitch meant that a 0 came across the wire when it shouldn't have. That's where the start and stop bits come in handy: if the bit that's meant to be a start bit is not a 0, or if the bit that's meant to be a stop bit is not a 1, we should stop what we're doing and not interpret our signal as a valid byte.

To accomplish this, we can make a simple state machine for our UART receiver. We want our receiver to begin in an IDLE state, and get pulled out of it when we receive a 0 on our wire. Then we want to progress through states to read our START bit, our DATA bits, and our STOP bit. But with the START and STOP bits, our steps should be interrupted if we read incorrect bits from the wire: when an incorrect START or STOP bit is detected, we should return to our IDLE state instead of continuing on. If both the START and STOP bits read correctly, we can progress to our TRANSMIT state and propogate our data appropriately.

UART FSM that will discard bad transmissions. Note how we can't say something is "bad" unless we're in the start or stop region. There's no way to determine if a packet is good or bad within the data region, we just have to trust the bits.

When in the START, DATA, and STOP states, your module should be counting clock cycles to measure each bit period, and counting how many bits have been received. In these states, wait for the midpoint of the bit period to sample data, and write it to a register to accumulate all the bits of the frame.

As you write your receiver module, be sure also to look back at the specification of UART from the last exercise to ensure you interpret bits in the correct order!

Parameters

  • parameter INPUT_CLOCK_FREQ: frequency, in Hz, of clk_in
  • parameter BAUD_RATE: bitrate for UART transmission, in bits/second

Inputs and Outputs

  • input wire clk_in: operational clock
  • input wire rst_in: signal to reset the module (active high)
  • input wire rx_wire_in: serial input signal, as described by UART
  • output logic new_data_out: single-cycle high as soon as we've verified that a byte was successfully received. No need to wait for the end of the full stop bit, so long as the message was valid.
  • output logic [7:0] data_byte_out: the byte received over the RX wire, valid when new_data_out is high

module uart_receive
Submit your tested UART receiver module here.
Code Skeleton
  No file selected