//
// Very simple asynchronous tty. Not really a "UART" since it isn't
// very "universal". It doesn't have to be.
//
// Currently only transmit is supported, 8-N-1, at a fixed speed.
// The baud rate is tty_clk/((divisor+1)*16).
//
// The registers are as follows:
//
// 0 - WO - data register (will be RW)
// 1 - RW - baud rate register (binary fraction of TTY_CLK/16 - 1)
// 2 - RO - status register
//          0 - transmitter idle (FIFO and shift register empty)
//          1 - transmit FIFO 3/4 empty
//	    2 - transmit FIFO 1/2 empty
//          3 - transmit FIFO 1/2 full (inverse of bit 2)
//          4 - transmit FIFO 3/4 full
// 3 - RW - interrupt enable register (status register mask)

module tty (
	    input	      rst_n,
	    input	      clk,

	    input	      valid,
	    input [3:0]       wstrb,
	    input [31:0]      wdata,
	    input [1:0]       addr,
	    output reg [31:0] rdata,
	    output reg	      irq,

	    output	      tty_txd
	    );

   `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 = 115200;
   parameter [31:0]	TTY_CLK  = 84000000;
   parameter		NCO_BITS = 24;

   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_data;
   wire [8:0] tx_usedw;

   fifo txfifo (
		.aclr ( ~rst_n ),
		.clock ( clk ),
		.data ( wdata ),
		.rdreq ( tx_rdack ),
		.sclr ( 1'b0 ),	// Flush FIFO command
		.wrreq ( tx_wrreq ),
		.empty ( tx_rdempty ),
		.full ( ),
		.q ( tx_data ),
		.usedw ( tx_usedw )
		);

   //
   // Transmitter
   //
   reg [3:0]  tx_phase;
   reg [3:0]  tx_bits;
   reg [9:0]  tx_sr = ~10'b0;		// Shift register
   assign tty_txd = 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_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)

   //
   // CPU interface
   //

   // Data (FIFO) register; normally addressed as byte
   // Protect against long pulses (edge detect)
   reg old_wstrb;
   always @(posedge clk)
     old_wstrb <= wstrb[0];

   assign tx_wrreq = valid & wstrb[0] & ~old_wstrb & (addr == 2'b00);

   // Status register definition
   localparam status_bits = 5;
   wire [status_bits-1:0] status;

   assign status[0] = tx_rdempty & (tx_bits == 4'd0);
   assign status[1] = tx_usedw[8:7] == 2'b00;
   assign status[2] = ~tx_usedw[8];
   assign status[3] = tx_usedw[8];
   assign status[4] = tx_usedw[8:7] == 2'b11;

   reg [status_bits-1:0]  irq_en;
   //
   // Control register writes.
   // Only full word writes are supported.
   //
   always @(negedge rst_n or posedge clk)
     if (~rst_n)
       begin
	  irq_en <= 5'b0;
       end
     else if (valid & (&wstrb))
       case (addr)
	 2'b01: divisor <= wdata[NCO_BITS-1:0];
	 2'b11: irq_en  <= wdata[status_bits-1:0];
       endcase // case (addr)

   // Read MUX
   always @(*)
     begin
	rdata = 32'b0;
	case (addr)
	  2'b01:   rdata[NCO_BITS-1:0]    = divisor;
	  2'b10:   rdata[status_bits-1:0] = status;
	  2'b11:   rdata[status_bits-1:0] = irq_en;
	  default: rdata = 32'b0;
	endcase // case (addr)
     end

   // Interrupt
   always @(negedge rst_n or posedge clk)
     if (~rst_n)
       irq <= 1'b0;
     else
       irq <= |(status & irq_en);

endmodule // tty
