`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