//
// Serial port "snoop" of the console (otherwise sent via USB.)
//
// The only receive support is BREAK.
//
// 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 (
	    input 	rst_n,
	    input 	clk,

	    input 	tx_valid,
	    input [7:0] tx_data,

	    input 	tty_rx,
	    output 	tty_tx,

	    output reg	rx_break,
	    output 	tx_full
	    );

   `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.
   //
   parameter [31:0]	BAUDRATE = 921600;
   parameter [31:0]	TTY_CLK  = 84000000;
   parameter		NCO_BITS = 24;
   parameter            BREAK_BITS = 19; // 2^19/(BAUDRATE*16) ~ 33 ms

   reg  [NCO_BITS-1:0]  divisor
			= round_div(BAUDRATE << NCO_BITS, TTY_CLK >> 4) - 1'b1;
   reg  [NCO_BITS-1:0]  nco_q;
   reg			tty_clk_en; // tty clock tick (clock enable)

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

   //
   // Tx FIFO
   //
   reg  tx_rdack;
   wire tx_wrreq;
   wire tx_rdempty;
   wire [7:0] tx_out_data;

   reg 	      tx_valid_q;

   always @(negedge rst_n or posedge clk)
     if (~rst_n)
       tx_valid_q <= 1'b0;
     else
       tx_valid_q <= tx_valid;

   fifo txfifo (
		.aclr ( ~rst_n ),
		.clock ( clk ),
		.data ( tx_data ),
		.rdreq ( tx_rdack ),
		.sclr ( 1'b0 ),	// Flush FIFO command
		.wrreq ( tx_valid & ~tx_valid_q ),
		.empty ( tx_rdempty ),
		.full ( tx_full ),
		.q ( tx_out_data ),
		.usedw ( )
		);

   //
   // Transmitter
   //
   reg [3:0]  tx_phase;
   reg [3:0]  tx_bits;
   reg [9:0]  tx_sr = ~10'b0;		// Shift register
   assign tty_tx = tx_sr[0];

   always @(negedge rst_n or posedge clk)
     if (~rst_n)
       begin
	  tx_phase <= 4'h0;
	  tx_bits  <= 4'd0;
	  tx_sr    <= ~10'b0;	// Line idle
	  tx_rdack <= 1'b0;
       end
     else
       begin
	  tx_rdack <= 1'b0;

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

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

		    if (tx_bits == 4'd0)
		      begin
			 if ( ~tx_rdempty )
			   begin
			      tx_sr[9:2] <= tx_out_data;
			      tx_sr[1]   <= 1'b0; // Start bit
			      // 10 = start bit + data + stop bit
			      tx_bits    <= 4'd10;
			      tx_rdack   <= 1'b1; // Remove from FIFO
			   end
		      end
		    else
		      begin
			 tx_bits <= tx_bits - 1'b1;
		      end // else: !if(tx_bits == 4'd0)
		 end // if (tx_phase == 4'hF)
	    end // if ( tty_clk_en )
       end // else: !if(~rst_n)

   //
   // BREAK detect
   //
   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
		 begin
		    rx_break_ctr <= rx_break_ctr_next[BREAK_BITS-1:0];
		    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)
   
endmodule // serial
