`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 09/21/2023 01:23:46 PM
// Design Name:
// Module Name: uart_rx
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// File Downloaded from http://www.nandland.com
//////////////////////////////////////////////////////////////////////
// This file contains the UART Receiver. This receiver is able to
// receive 8 bits of serial data, one start bit, one stop bit,
// and no parity bit. When receive is complete o_rx_dv will be
// driven high for one clock cycle.
//
// input i_Clocks_per_Bit = (Frequency of i_Clock)/(Frequency of UART)
// Example: 100 MHz Clock, 1MHz baud UART
// i_Clocks_per_Bit = (100,000,000)/(1,000,000) = 100
//
// when input i_Rx_Serial makes a transition (active low) then this
// module decodes the serial stream and presents an output byte
// note: there must be 1 start, 1 stop, and no parity bits
// (this can be changed of course)
//
// o_Rx_DV = signal that new data is available (1 clock tick long)
// o_Rx_Byte = that new data
//
// could also add an acknowledge but that's for another version of this
//
module uart_rx (
input i_Clock,
input [15:0] i_Clocks_per_Bit,
input i_Reset,
input i_Rx_Serial,
output o_Rx_DV,
output [7:0] o_Rx_Byte,
output [7:0] o_debug
);
localparam s_IDLE = 3'b000;
localparam s_RX_START_BIT = 3'b001;
localparam s_RX_DATA_BITS = 3'b010;
localparam s_RX_STOP_BIT = 3'b011;
localparam s_CLEANUP = 3'b100;
reg r_Rx_Data_R = 1'b1;
reg r_Rx_Data = 1'b1;
reg [11:0] r_Clock_Count = 0;
reg [2:0] r_Bit_Index = 0; // which of 8 max data bits
reg [7:0] r_Rx_Byte = 0;
reg r_Rx_DV = 0;
reg [2:0] r_SM_Main = 0;
// assign o_debug = {r_Bit_Index[2:0],r_SM_Main[2:0],o_Rx_DV,i_Rx_Serial};
assign o_debug = {1'b0,i_Clock,i_Reset,r_SM_Main[2:0],o_Rx_DV,i_Rx_Serial};
// Purpose: Double-register the incoming data.
// This allows it to be used in the UART RX Clock Domain.
// (It removes problems caused by metastability)
always @(posedge i_Clock)
begin
r_Rx_Data_R <= i_Rx_Serial;
r_Rx_Data <= r_Rx_Data_R;
end
// Purpose: Control RX state machine
always @(posedge i_Clock or posedge i_Reset)
if (i_Reset) r_SM_Main <= s_IDLE;
else begin
case (r_SM_Main)
s_IDLE :
begin
r_Rx_DV <= 1'b0;
r_Clock_Count <= 0;
r_Bit_Index <= 0;
if (r_Rx_Data == 1'b0) // Start bit detected
r_SM_Main <= s_RX_START_BIT;
else
r_SM_Main <= s_IDLE;
end
// Check middle of start bit to make sure it's still low
s_RX_START_BIT :
begin
if (r_Clock_Count == (i_Clocks_per_Bit-1)/2)
begin
if (r_Rx_Data == 1'b0)
begin
r_Clock_Count <= 0; // reset counter, found the middle
r_SM_Main <= s_RX_DATA_BITS;
end
else
r_SM_Main <= s_IDLE;
end
else
begin
r_Clock_Count <= r_Clock_Count + 1;
r_SM_Main <= s_RX_START_BIT;
end
end // case: s_RX_START_BIT
// Wait i_Clocks_per_Bit-1 clock cycles to sample serial data
s_RX_DATA_BITS :
begin
if (r_Clock_Count < i_Clocks_per_Bit-1)
begin
r_Clock_Count <= r_Clock_Count + 1;
r_SM_Main <= s_RX_DATA_BITS;
end
else
begin
r_Clock_Count <= 0;
r_Rx_Byte[r_Bit_Index] <= r_Rx_Data;
// Check if we have received all bits
if (r_Bit_Index < 7)
begin
r_Bit_Index <= r_Bit_Index + 1;
r_SM_Main <= s_RX_DATA_BITS;
end
else
begin
r_Bit_Index <= 0;
r_Clock_Count <= 0;
r_SM_Main <= s_RX_STOP_BIT;
end
end
end // case: s_RX_DATA_BITS
// Receive Stop bit. Stop bit = 1
s_RX_STOP_BIT :
begin
// Wait i_Clocks_per_Bit-1 clock cycles for Stop bit to finish
if (r_Clock_Count < i_Clocks_per_Bit-1)
begin
r_Clock_Count <= r_Clock_Count + 1;
r_SM_Main <= s_RX_STOP_BIT;
end
else
begin
r_Rx_DV <= 1'b1;
r_Clock_Count <= 0;
r_SM_Main <= s_CLEANUP;
end
end // case: s_RX_STOP_BIT
// Stay here 1 clock
s_CLEANUP :
begin
r_SM_Main <= s_IDLE;
r_Rx_DV <= 1'b0;
end
default :
r_SM_Main <= s_IDLE;
endcase
end
assign o_Rx_DV = r_Rx_DV;
assign o_Rx_Byte = r_Rx_Byte;
endmodule // uart_rx
All rights reserved. No part of this publication may be reproduced, distributed, or transmitted in any
form or by any means, including photocopying, recording, or other electronic or mechanical methods, without
prior written permission, except in the case of brief quotations embodied in critical
reviews and certain other noncommercial uses permitted by copyright law.
Unless indicated otherwise, any lecture handouts, exams, homework and exam solutions,
and the lectures themselves (including audio and video recordings) are copyrighted by
me and may not be distributed or reproduced for anything other than your personal use
without my written permission.
Last updated October, 2023 Drew Baden