fpga4fun.comwhere FPGAs are fun

Serial interface 4 - RS-232 receiver

We are building an "async receiver":

Our implementation works like that:

Note that "data" is valid only when "data_ready" is asserted. The rest of the time, don't use it as new data may come that shuffles it.


An asynchronous receiver has to somehow get in-sync with the incoming signal (it normally doesn't have access to the clock used by the transmitter).

Receivers typically oversample the incoming signal at 16 times the baud rate. We use 8 times here... For 115200 bauds, that gives a sampling rate of 921600Hz.

Let's assume that we have a "Baud8Tick" signal available, asserted 921600 times a second.

The design

First, the incoming "RxD" signal has no relationship with our clock.
We use two D flip-flops to oversample it, and synchronize it to our clock domain.

reg [1:0] RxD_sync;
always @(posedge clk) if(Baud8Tick) RxD_sync <= {RxD_sync[0], RxD};

We filter the data, so that short spikes on the RxD line aren't mistaken with start bits.

reg [1:0] RxD_cnt;
reg RxD_bit;

always @(posedge clk)
  if(RxD_sync[1] && RxD_cnt!=2'b11) RxD_cnt <= RxD_cnt + 1;
  if(~RxD_sync[1] && RxD_cnt!=2'b00) RxD_cnt <= RxD_cnt - 1;

  if(RxD_cnt==2'b00) RxD_bit <= 0;
  if(RxD_cnt==2'b11) RxD_bit <= 1;

A state machine allows us to go through each bit received, once a "start" is detected.

reg [3:0] state;

always @(posedge clk)
  4'b0000: if(~RxD_bit) state <= 4'b1000; // start bit found?
  4'b1000: if(next_bit) state <= 4'b1001; // bit 0
  4'b1001: if(next_bit) state <= 4'b1010; // bit 1
  4'b1010: if(next_bit) state <= 4'b1011; // bit 2
  4'b1011: if(next_bit) state <= 4'b1100; // bit 3
  4'b1100: if(next_bit) state <= 4'b1101; // bit 4
  4'b1101: if(next_bit) state <= 4'b1110; // bit 5
  4'b1110: if(next_bit) state <= 4'b1111; // bit 6
  4'b1111: if(next_bit) state <= 4'b0001; // bit 7
  4'b0001: if(next_bit) state <= 4'b0000; // stop bit
  default: state <= 4'b0000;

Notice that we used a "next_bit" signal, to go from bit to bit.

reg [2:0] bit_spacing;

always @(posedge clk)
  bit_spacing <= 0;
  bit_spacing <= bit_spacing + 1;

wire next_bit = (bit_spacing==7);

Finally a shift register collects the data bits as they come.

reg [7:0] RxD_data;
always @(posedge clk) if(Baud8Tick && next_bit && state[3]) RxD_data <= {RxD_bit, RxD_data[7:1]};

The complete code can be found here.
It has a few improvements; follow the comments in the code.