![]() |
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 doesn't have access to the clock used during transmission).
Receivers typically oversample the incoming signal at 16 times the baud rate. Here we use 8 times. At 115200 bauds, that gives a sampling rate of 921600Hz.
Let's assume that we have a "Baud8Tick" signal available, asserted 921600 times a second.
First, the incoming "RxD" signal has no relationship with our clock.
We use two D-flipflops to oversample it, and synchronize it to our clock.
|
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(Baud8Tick) begin if(RxD_sync[1] && RxD_cnt!=2'b11) RxD_cnt <= RxD_cnt + 1; else if(~RxD_sync[1] && RxD_cnt!=2'b00) RxD_cnt <= RxD_cnt - 1; if(RxD_cnt==2'b00) RxD_bit <= 0; else if(RxD_cnt==2'b11) RxD_bit <= 1; end |
A state machine allows us to go through each bit received, once a "start" is detected.
|
reg [3:0] state; always @(posedge clk) if(Baud8Tick) case(state) 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; endcase |
Notice that we used a "next_bit" signal, to go from bit to bit.
|
reg [2:0] bit_spacing; always @(posedge clk) if(state==0) bit_spacing <= 0; else if(Baud8Tick) 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]}; |
That's it!
The complete code can be found here.
It has a few improvements; follow the comments in the code.