//
//								uart.v - 8-bit UART
//
//					(C) Copyright 2004-2008 John B. Stephensen
//
// This Verilog source file and all its derivatives are licensed only for
// personal non-profit educational use in the Amateur Radio Service and
// the license is not transferrable. The information is provided as-is for
// experimental purposes and the author does not warranty its freedom
// from defects or its suitability for any specific application.
//
// This module implements a UART with 1 start bit, 8 data bits and 1 stop bit.
// Register 0 id used for data I/O. Register 1 is used for status and to set
// the baud rate. CLK is divided by baud+1.
//
// Command Registers:
//    15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
//   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 0 |                               |             Data              |
//   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 1 |                           Baud Rate                           |
//   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//
// Status Registers:
//    15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
//   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 0 |                           |FE |             Data              |
//   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// 1 |                                               |TXE|TXR|RXF|RXR|
//   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//
//		FE = framing error
//		TXR = transmitter FIFO not full
//		TXE = transmitter FIFO empty
//		RXF = receiver FIFO full (oveflow)
//		RXR = receiver FIFO not empty
//
// 74 slices are used and the maximum clock rate is 146 MHz.
//
// Normal warnings:
//		<ioaddr<2:1>> is never used.
//
// History:
//		3-3-09	sync. output for 2-cycle input inst.
//
module uart(
    input iocs,			// select this module
    input [2:0] ioaddr,	// internal register addresses
    input [15:0] din,	// parallel data input port
    input iowr,
    output [15:0] dout,	// parallel data output port
    input iord,
	 input rxd,				// receved data input
	 output txd,			// transmit data output
	 output txen,			// transmit enable
    input clk,				// master clock
    input rst
    );
// internal signals
reg [15:0] baud,brg;		// 16x baud rate select
wire en;						// enable UART logic at 16x baud rate
wire rxe,rxf,txe,txf;	// FIFO status
reg [8:0] rxsr;			// receiver shift register
reg [8:0] txsr;			// transmitter shift register
reg rxd1;					// delayed RXD to detect beginning of start bit
reg [8:1] sdly;			// start delay logic (shift register)
wire start;
reg [7:0] rctr,tctr;		// sample period counters
wire rnz,rxc,tnz,txc;	// clocks
wire rstop,tstop;			// terminal count at stop bit position
wire [8:0] rx;				// receiver FIFO output data
reg [8:0] omux;			// receiver output multiplexer
wire [7:0] tx;				// transmitter FIFO output
// address decoding - upper 3 bits ignored
wire r0,r1;
assign r0 = iocs & (ioaddr[0] == 1'b0);
assign r1 = iocs & (ioaddr[0] == 1'b1);
// baud rate generator - divide clock by 16-bit constant
always @ (posedge clk)
begin
	if (iowr & r1) baud <= din;	// write divisor
	if (rst|en) brg <= 0;
	else brg <= brg + 1;
end
assign en = (brg == baud);	// reload when max
// receiver logic to detect start bit, generate data sampling clock (RXC)
// and de-serialize data
always @ (posedge clk)
begin
	if (en) rxd1 <= rxd;								// detect start bit leading edge
	if (en) sdly <= {sdly[7:1],rxd1 & ~rxd};	// and apply 1/2-bit delay
	if (rst | (en & rstop)) rctr <= 0;			// generate clock
	else if (en & (rnz | start)) rctr <= rctr + 1;
	if (en & rxc) rxsr <= {rxd,rxsr[8:1]};		// sample data
end
assign rnz = |rctr;					// receiver sample counter non-zero
assign rxc = (rctr[3:0] == 15);	// sample at bit cell centers
assign start = sdly[8] & ~rxd;	// start sampling in middle of start bit
assign rstop = (rctr == 144);		// stop sampling in middle of stop bit
// receive FIFO
fifo16x9s rxfifo (
	.pdi({~rxsr[8],rxsr[7:0]}),	// receiver shift register (invert stop bit)
	.iv(en & rstop),					// stop bit detected
	.oe(iord & r0),					// read command
	.pdo(rx),							// received byte out
	.ov(),
	.empty(rxe),						// no data to read
	.full(rxf),							// receiver overflow
	.clk(clk),
	.rst(rst)
	);
// connect output
always @ (posedge clk)
	if (iord) omux <= ioaddr[0] ? {5'b00000,txe,~txf,rxf,~rxe} : rx;
assign dout = {7'b0000000,omux};
// transmit FIFO
fifo16x8s txfifo (
	.pdi(din[7:0]),			// data bus
	.iv(iocs & iowr & r0),	// write data byte
	.oe(en & tstop),			// get new byte to transmit
	.pdo(tx),					// byte to transmit
	.ov(),
	.empty(txe),				// no data to send
	.full(txf),					// transmitter overflow
	.clk(clk),
	.rst(rst)
	);
// tranmitter shift register - shift MSB to LSB
always @ (posedge clk)
begin
	if (rst | (en & tstop)) tctr <= 0;					// generate clock
	else if (en & (tnz | ~txe)) tctr <= tctr + 1;
	if (en & txc & ~tnz & ~txe) txsr <= {tx,1'b0};	// load data and start bit
	else if (en & txc) txsr <= {1'b1,txsr[8:1]};		// fill with one's
end
assign txen = tnz | ~txe;			// enable when FIFO not empty or still shifting
assign tnz = |tctr;					// receiver sample counter non-zero
assign txc = (tctr[3:0] == 0);	// shift every 16th clock
assign tstop = (tctr == 159);		// stop on 10th (stop) bit
assign txd = txsr[0];
endmodule
