//
// Simple serial port.
//
// The transmission rate is fixed to 1 Mbps.
//
// If the output FIFO is full, an output signal is asserted, which
// is expected to be used together with DTR to provide blocking I/O
// if desired.
//

module serial
  #(
    parameter logic  ENABLE_RX_DATA    = 1'b1,
    parameter logic  ENABLE_RX_BREAK   = 1'b1,
    parameter logic  ENABLE_TX_DATA    = 1'b1,
    parameter logic  ENABLE_TX_BREAK   = 1'b1,
    parameter logic  BAUDRATE_SETTABLE = 1'b0,
    parameter [31:0] BAUDRATE          = 115200,
    parameter [31:0] TTY_CLK           = 84000000,
    parameter        NCO_BITS          = 24,
    parameter        BREAK_BITS        = 16 // Bit times for BREAK detect
    )
   (
    input tri1	  rst_n,
    input	  clk,

    output	  tty_tx,
    input	  tty_rx,

    input	  tx_wstrb,
    input [7:0]   tx_data,
    input	  tx_break, // BREAK is asserted as long as this is true
    output	  tx_full,
    output	  tx_empty,
    input tri0	  tx_flush,

    input	  rx_rstrb,
    output [7:0]  rx_data,
    output	  rx_break,
    output	  rx_full,
    output	  rx_empty,
    input tri0	  rx_flush,

    input [31:0]  divisor_wdata, // If divisor is settable
    input tri0	  divisor_wstrb,
    output [31:0] divisor
    );

   `include "functions.sv"	// For ModelSim

   //
   // Baud rate generator; produces a clock enable synchronous
   // with clk.  This is based on a numerically controlled oscillator
   // (NCO); the baudrate is given as a binary fraction of the input
   // clock rate divided by the oversampling rate (16); for practical
   // reasons represented minus one LSB so 0x0.ffffff -> 1
   // and 0 -> 0x0.000001.
   //
   // The term "divisor" here is probably misleading, since it is
   // actually a fixed-point *multiplier* which is <= 1.
   //
   localparam [NCO_BITS-1:0] default_divisor =
		  round_div(BAUDRATE << NCO_BITS, TTY_CLK >> 4) - 1'b1;
   reg  [NCO_BITS-1:0]	divisor_q = default_divisor;
   reg  [NCO_BITS-1:0]  nco_q;
   reg			tty_clk_en; // tty clock tick (clock enable)

   assign divisor = divisor_q;

   always @(posedge clk)
     { tty_clk_en, nco_q } <= nco_q + divisor_q + 1'b1;

   always @(negedge rst_n or posedge clk)
     if (~rst_n)
       divisor_q <= default_divisor;
     else if ( BAUDRATE_SETTABLE & divisor_wstrb )
       divisor_q <= divisor_wdata[NCO_BITS-1:0];

   //
   // **** Transmitter section ****
   //
   reg		tx_rdack;
   wire [7:0]	tx_out_data;

   //
   // FIFO
   //
   generate if (ENABLE_TX_DATA)
     begin
	//
	// Tx FIFO
	//

	reg tx_wstrb_q;
	always @(negedge rst_n or posedge clk)
	  if (~rst_n)
	    tx_wstrb_q <= 1'b0;
	  else
	    tx_wstrb_q <= tx_wstrb;

	fifo txfifo (
		     .aclr ( ~rst_n ),
		     .clock ( clk ),
		     .data ( tx_data ),
		     .wrreq ( tx_wstrb & ~tx_wstrb_q ),
		     .sclr ( tx_flush ),
		     .empty ( tx_empty ),
		     .full ( tx_full ),
		     .rdreq ( tx_rdack ),
		     .q ( tx_out_data ),
		     .usedw ( )
		     );
     end // if (ENABLE_TX_DATA)
   else
     begin
	assign tx_empty    = 1'b1;
	assign tx_full     = 1'b1;
	assign tx_out_data = 8'hxx;
     end
   endgenerate

   //
   // Transmitter
   //
   generate if (ENABLE_TX_DATA | ENABLE_TX_BREAK)
     begin
	reg [3:0] tx_phase;
	reg [3:0] tx_ctr;
	reg [9:0] tx_sr = ~10'b0;
	reg	  tx_sending_break;

	always @(negedge rst_n or posedge clk)
	  if (~rst_n)
	    begin
	       tx_phase <= 4'h0;
	       tx_ctr  <= 'd0;
	       tx_sr    <= 10'h3ff;	// Line idle
	       tx_rdack <= 1'b0;
	       tty_tx   <= 1'b1;
	       tx_sending_break <= 1'b0;
	    end
	  else
	    begin
	       tx_rdack <= 1'b0;
	       tty_tx   <= tx_sr[0] & ~tx_sending_break;

	       if ( tty_clk_en )
		 begin
		    tx_phase <= tx_phase + 1'b1;

		    if (&tx_phase)
		      begin
			 tx_sr[8:0] <= tx_sr[9:1];
			 tx_sr[9]   <= 1'b1; // Stop bit/idle

			 if (|tx_ctr)
			   begin
			      tx_ctr <= tx_ctr - 1'b1;
			   end
			 else if ( ENABLE_TX_BREAK &
				   (tx_break | tx_sending_break) )
			   begin
			      // Make sure we get an idle bit after break
			      tx_sending_break <= tx_break;
			   end
			 else if ( ~tx_empty )
			   begin
			      tx_sr[9:2] <= tx_out_data;
			      tx_sr[1]   <= 1'b0; // Start bit
			      // 10 = start bit + data + stop bit
			      tx_ctr     <= 'd10;
			      tx_rdack   <= 1'b1; // Remove from FIFO
			   end
		      end // if (tx_phase == 4'hF)
		 end // if ( tty_clk_en )
	    end // else: !if(~rst_n)
     end // if (ENABLE_TX_DATA | ENABLE_TX_BREAK)
   else
     begin
	assign tty_tx = 1'b1;
     end // else: !if(ENABLE_TX_DATA | ENABLE_TX_BREAK)
   endgenerate

   //
   // **** Receiver section ****
   //

   //
   // Bit phase counter
   //
   generate if (ENABLE_RX_DATA | ENABLE_RX_BREAK)
     begin
	reg [3:0] rx_phase;
	reg	  rx_phase_start = 1'b0;

	always @(negedge rst_n or posedge clk)
	  if (~rst_n)
	    rx_phase <= 4'b0;
	  else if ( rx_phase_start )
	    rx_phase <= 4'h8;	// Start bit flank detected
	  else
	    rx_phase <= rx_phase + tty_clk_en;
     end // if (ENABLE_RX_DATA | ENABLE_RX_BREAK)
   endgenerate

   //
   // BREAK detect
   //

   generate if (ENABLE_RX_BREAK)
     begin
	reg  [BREAK_BITS-1:0] rx_break_ctr;
	wire [BREAK_BITS:0]   rx_break_ctr_next = rx_break_ctr + 1'b1;

	always @(negedge rst_n or posedge clk)
	  if (~rst_n)
	    begin
	       rx_break_ctr <= 'b0;
	       rx_break     <= 1'b0;
	    end
	  else
	    begin
	       if ( tty_clk_en )
		 begin
		    if ( tty_rx )
		      begin
			 // Rx high = not a break
			 rx_break_ctr <= 'b0;
			 rx_break     <= 1'b0;
		      end
		    else if ( &rx_phase )
		      begin
			 rx_break_ctr <= rx_break_ctr_next;
			 rx_break     <= rx_break | rx_break_ctr_next[BREAK_BITS];
		      end // else: !if(tty_rxd)
		 end // if ( tty_clk_en )
	    end // else: !if(~rst_n)
     end // if (ENABLE_RX_BREAK)
   else
     begin
	assign rx_break = 1'b0;
     end
   endgenerate

   //
   // Data receive
   //
   generate if (ENABLE_RX_DATA)
     begin
	reg rx_recv_strb;
	reg rx_rstrb_q;

	fifo rxfifo (
		     .aclr ( ~rst_n ),
		     .clock ( clk ),
		     .rdreq ( ~rx_rstrb & rx_rstrb_q ), // Advance after read
		     .q ( rx_data ),
		     .sclr ( rx_flush ),
		     .empty ( rx_empty ),
		     .full ( rx_full ),
		     .wrreq ( rx_recv_strb ),
		     .data ( rx_sr ),
		     .usedw ( )
		     );

	reg [3:0] rx_ctr;
	reg [2:0] rx_bits;
	reg [7:0] rx_sr;
	reg	  tty_clk_en_q;

	always @(negedge rst_n or posedge clk)
	  if (~rst_n)
	    begin
	       rx_ctr         <= 4'b0;
	       rx_bits        <= 3'b111;
	       rx_sr          <= 8'hxx;
	       tty_clk_en_q   <= 1'b0;
	       rx_rstrb_q      <= 1'b0;
	       rx_recv_strb    <= 1'b0;
	       rx_phase_start <= 1'b0;
	    end
	  else
	    begin
	       rx_rstrb_q    <= rx_rstrb;
	       tty_clk_en_q <= tty_clk_en;
	       rx_recv_strb  <= 1'b0;

	       if ( tty_clk_en )
		 rx_bits <= { rx_bits[1:0], tty_rx };

	       if ( tty_clk_en_q )
		 begin
		    if ( !rx_ctr )
		      begin
			 if ( rx_bits == 3'b100 )
			   begin
			      rx_ctr <= 4'd10;
			      rx_phase_start <= 1'b1;
			   end
		      end
		    else if ( !rx_phase )
		      begin
			 rx_sr[6:0] <= rx_sr[7:1];
			 // Majority vote of three samples
			 rx_sr[7] <= (rx_bits[1] & rx_bits[0]) |
				     (rx_bits[2] & rx_bits[0]) |
				     (rx_bits[2] & rx_bits[1]);

			 rx_ctr <= rx_ctr - 1'b1;
			 // Push to FIFO if this was the bit before stop
			 rx_recv_strb <= rx_ctr == 4'd2;
		      end // if ( &rx_phase )
		 end // if ( tty_clk_en_q )
	    end // else: !if(~rst_n)
     end // if (ENABLE_RX_DATA)
   endgenerate
endmodule // serial
