`timescale 1ns / 1ps
/**
@brief A single 8-bit, no parity, 1 stop bit serial port.
This is the raw UART and does not have a FIFO etc. The parent module must read
data the cycle after it becomes available and process or store it.
@param clk 20.48 MHz clock signal
@param clkdiv Clock divisor for baud rate generator (115200 baud = 178)
@param tx Outbound serial data line
@param txin 8-bit wide bus containing data to be transmitted
@param txrdy Bring high for 1 clk when data is ready to be transmitted
@param txactive Indicates if transmitter is busy
@param rx Inbound serial data line
@param rxout 8-bit wide bus containing data recieved
@param rxrdy Goes high for one clk when valid data is present on rxout
@param rxactive Indicates if reciever is busy
*/
module UART(clk, clkdiv, tx, txin, txrdy, txactive, rx, rxout, rxrdy, rxactive);
input wire clk;
input wire[15:0] clkdiv;
input wire rx;
//1/4 of the clock divisor (for 90 degree phase offset)
wire[13:0] clkdiv_offset;
assign clkdiv_offset = clkdiv[15:2];
///////////////////////////////////////////////////////////////////////////////////////////////
//Receiver
reg[15:0] rxbrg;
output reg rxactive;
reg[4:0] rxbitcount;
reg[7:0] rxbuf;
output reg[7:0] rxout;
output reg rxrdy;
initial begin
rxbrg <= 0;
rxactive <= 0;
rxbitcount <= 0;
rxbuf <= 0;
rxout <= 0;
rxrdy <= 0;
end
always @(posedge clk) begin
//Clear data from output after one clock
if(rxrdy) begin
rxrdy <= 0;
rxout <= 0;
end
//If not currently recieving, look for falling edge on RX (start bit)
if(!rxactive) begin
if(rx == 0) begin
//Falling edge, start receiving after 1.25 bit period
//We want to sample 90 degrees out of phase with the original signal to get nice stable values
rxactive <= 1;
rxbrg <= clkdiv_offset + clkdiv;
rxbitcount <= 0;
end
end
//Currently recieving
else begin
rxbrg <= rxbrg - 1;
//Time to sample a new bit
if(rxbrg == 0) begin
//If we are on bits 0 through 7 (not the stop bit) read the bit into the rxbuf and bump the bit count, then reset the baud generator
if(rxbitcount < 8) begin
rxbuf <= {rx, rxbuf[7:1]};
rxbitcount <= rxbitcount + 1;
rxbrg <= clkdiv;
end
//Stop bit
else begin
//Should always be 1, print warning in sim if this isnt the case
if(rx != 1)
$display("[UART] Warning - stop bit isn't zero");
//We're done reading
rxbitcount <= 0;
rxactive <= 0;
//Data is ready
rxout <= rxbuf;
rxrdy <= 1;
$display("[UART] Read byte 0x%02x - '%c'", rxbuf, rxbuf);
end
end
end
end
///////////////////////////////////////////////////////////////////////////////////////////////
//Transmitter
output reg tx;
input wire txrdy;
input wire [7:0] txin;
reg[15:0] txbrg;
reg[7:0] txbuf;
output reg txactive;
reg [3:0] txbitcount;
initial begin
tx <= 1;
txbuf <= 0;
txactive <= 0;
txbrg <= 0;
txbitcount <= 0;
end
always @(posedge clk) begin
//Time to start sending a new byte?
if(txrdy) begin
//Already transmitting? Drop the byte, nothing we can do here.
//TODO: add a FIFO
if(txactive) begin
$display("[UART] Warning - transmit buffer overflow, byte dropped");
end
//Nope, set up a transmission
else begin
if(txin > 8'h20) begin
$display("[UART] sending byte 0x%02x - '%c'", txin, txin);
end
else begin
$display("[UART] sending byte 0x%02x", txin);
end
txbuf <= txin;
txactive <= 1;
txbitcount <= 0;
//Send the start bit immediately
tx <= 0;
txbrg <= clkdiv;
end
end
//Currently transmitting?
if(txactive) begin
//Done with this bit?
if(txbrg == 0) begin
//Are we still sending normal data bits?
//Send the next data bit (LSB first)
if(txbitcount < 8) begin
txbitcount <= txbitcount + 1;
tx <= txbuf[0];
txbuf <= {1'b0, txbuf[7:1]};
txbrg <= clkdiv;
end
//Time to send the stop bit?
//Send it
else if(txbitcount == 8) begin
txbitcount <= 9;
tx <= 1;
txbrg <= clkdiv;
end
//Done sending? Reset stuff
else if(txbitcount == 9) begin
txbitcount <= 0;
txactive <= 0;
end
end
//Nope, just keep count
else begin
txbrg <= txbrg - 1;
end
end
end
endmodule