// Chuck Benz, Hollis, NH   Copyright (c)2006
//
// The information and description contained herein is the
// property of Chuck Benz.
//
// Permission is granted for any reuse of this information
// and description as long as this copyright notice is
// preserved.  Modifications may be made as long as this
// notice is preserved.

// serdebug is a simple module that can be added to any design
// to provide an RS232 port with a simple monitor allowing
// read/write access to a memory space.  A UART provides serial
// connectivity, and an rx state machine looks for packets in
// the form of:
//   Xcrraaaaaaaadddddddddddd
// where X is the character 'X' or 'x'
//       c is a character specifying the command (A, B, G, and H are defined)
//       rr is two characters as hex digits representing an optional crc8
//       aaaaaaaa is 8 characters as hex digits representing a 32 bit address
//       dddddddddddddddd is 16 chars as hex digits representing 64 data bits
// the hex digits should be 0-9 and a-f or A-F.
// at any position, a space or _ can be inserted to aid readability
//   The point of this format is that it's usable from a terminal, but also
// usable from a program sending packets to the logic.

module cbaf_serdebug (
		      input      clk33,
		      input      resetl,

		      // serial lines
		      input      rxd,
		      output     txd,
		      
		      // external memory
		      output reg [31:0] mem_address,
		      output reg [63:0] mem_wrdata,
		      output reg        mem_read,
		      output reg        mem_write,
		      input [63:0]      mem_rddata,
		      input             mem_ack) ;
   
   wire       uart_tx_rege, uart_tx_bufe ;
   reg [7:0]  uart_txdata ;
   reg 	      uart_load, uart_txenable, uart_rxenable ;
   reg [11:0] uart_div ;

   reg [5:0]  tx_state, rx_state ;

   reg 	      rxdQ ;
   wire [7:0] uart_datain ;
   wire       uart_drdy ;
   reg [3:0]  val_line ;
   reg 	      do_cmd, snd_ans ;
   reg [23:0] txline ;
   reg 	      echochar ;
   reg 	      checkcrc ;
   reg [31:0] rxaddress ;
   reg [63:0] rxdata, answer_data ;
   reg [7:0]  rxcrc, rxtype ;
   reg [15:0] writecnt, scratch ;
   reg 	      crcsupported ;
//   reg [31:0] count ;
   
   wire [3:0] datain_nibble = uart_datain[6] ? 
			      (uart_datain[2:0] + 9) : (uart_datain[3:0]) ;

   always @ (posedge clk33 or negedge resetl)
     if (~resetl) begin

//	count <= 0 ;
	uart_div <= 0 ;
	uart_txenable <= 0 ;
	uart_rxenable <= 0 ;

	uart_load <= 0 ;
	uart_txdata <= 0 ;
	tx_state <= 0 ;
	rx_state <= 0 ;
	rxdQ <= 0 ;
	val_line <= 4 ;
	do_cmd <= 0 ;
	txline <= 24'h0d4243 ;
	echochar <= 1 ;
	checkcrc <= 0 ;
	rxaddress <= 0 ;
	rxdata <= 0 ;
	rxcrc <= 0 ;
	rxtype <= 0 ;
	snd_ans <= 0 ;
	answer_data <= 0 ;
	crcsupported <= 0 ;
	writecnt <= 0 ;
	scratch <= 0 ;
     end
     else begin
	// if clk is 100 MHz... need divide by 64M, bit [25]
	// uart_load is divided 100 MHz to the Tx clk rate...
	// for 57600, divide by 108 * 16 ...
	// for 115200, divide by 54 * 16 ...
	// but from 33 MHz, 18 * 16 for 115200...

	if (uart_div[4:0] == 17) begin
	   uart_div[4:0] <= 0 ;
	   uart_div[11:8] <= uart_div[11:8] + 1 ;
	end
	else
	  uart_div[4:0] <= uart_div[4:0] + 1 ;
	uart_div[7:5] <= 0 ;
	uart_txenable <= (uart_div == 0) ;
	uart_rxenable <= (uart_div[4:0] == 0) ;

	// tx state machine... send "hello\n" every 20 seconds
/*
	count <= count + 1 ;
	case (tx_state)
	  0: if (count[30:0] == 0)
	    tx_state <= 1 ;
	  1, 3, 5, 7, 9, 11, 13: 
	    if (uart_tx_bufe) begin
	       uart_load <= 1 ;
	       case (tx_state)
		 1: uart_txdata <= 8'h68 ; // h
		 3: uart_txdata <= 8'h65 ; // e
		 5: uart_txdata <= 8'h6c ; // l
		 7: uart_txdata <= 8'h6c ; // l
		 9: uart_txdata <= 8'h6f ; // o
		 11: uart_txdata <= 8'h0a ; // \n
		 13: uart_txdata <= 8'h0d ; // \r
	       endcase
	       tx_state <= tx_state + 1 ;
	    end
	  15:
	    begin
	       tx_state <= 0 ;
	    end
	  default:
	    begin
	       uart_load <= 0 ;
	       tx_state <= tx_state + 1 ;
	    end
	endcase
*/
	// tx state machine... send from txline or from answer_data
	uart_load <= 0 ;
	case (tx_state)
	  0: 
	    if (val_line > 0) 
	      tx_state <= 1 ;
	    else if (snd_ans)
	      tx_state <= 2 ;
	  1:
	    if (uart_tx_bufe && ~uart_load) begin
	       uart_load <= 1 ;
	       uart_txdata <= txline[7:0] ;
	       txline <= txline >> 8 ;
	       if (val_line == 1)
		 tx_state <= 0 ;
	       val_line <= val_line - 1 ;
	    end
	  default:
	    if (uart_tx_bufe && ~uart_load) begin
	       uart_load <= 1 ;
	       case (tx_state)
		 2: uart_txdata <= 8'h58 ;
		 3: uart_txdata <= 8'h30 ; // '0', 0x30 - answer type...
		 4: uart_txdata <= 8'h30 ; // will be crc..
		 5: uart_txdata <= 8'h30 ; // will be crc..
		 6, 7, 8, 9, 10, 11, 12, 13: 
		   uart_txdata <= 8'h30 ;
		 30: uart_txdata <= 8'h0d ;
		 31: uart_txdata <= 8'h0a ;
		 default: 
		   uart_txdata <= answer_data[63:60] +
			     ((answer_data[63:60] > 9) ? 8'h37 : 8'h30) ;
	       endcase
	       if (tx_state > 13)
		 answer_data <= answer_data << 4 ;
	       if (tx_state == 31)
		 tx_state <= 0 ;
	       else
		 tx_state <= tx_state + 1 ;
	       if (tx_state == 29)
		 snd_ans <= 0 ;
	    end
	endcase

	if (~crcsupported)
	  checkcrc <= 0 ;

	mem_write <= do_cmd && (rxtype == 8'h48) && ~mem_ack ;
	mem_read  <= do_cmd && (rxtype == 8'h42) && ~snd_ans && ~mem_ack ;
	mem_address <= rxaddress ;
	mem_wrdata <= rxdata ;
	
	// ops state machine
	if (do_cmd)
	  case (rxtype)
	    8'h41: // 'A' - read our only csr
	      begin
		 if (~snd_ans) begin
		    do_cmd <= 0 ;
		    snd_ans <= 1 ;
		    answer_data <= {16'hcbaf, scratch, writecnt,
				    13'b0, echochar, crcsupported, checkcrc} ;
		 end
	      end

	    8'h42: // 'B' read of memory
	      begin
		 if (~snd_ans && mem_ack) begin
		    do_cmd <= 0 ;
		    snd_ans <= 1 ;
		    answer_data <= mem_rddata ;
		 end
	      end
	      
	    8'h47: // 'G' - write to csr.
	      begin
		 do_cmd <= 0 ;
		 if (rxaddress == 0) begin
		    checkcrc <= rxdata[0] ;
		    echochar <= rxdata[2] ;
		    if (rxdata[31:16] != 0)
		      writecnt <= 0 ;
		    else
		      writecnt <= writecnt + 1 ;
		    scratch <= rxdata[47:32] ;
		 end
	      end

	    8'h48: // 'H' - write to memory
	      begin
		 if (mem_ack) begin
		    do_cmd <= 0 ;
		    writecnt <= writecnt + 1 ;
		 end
	      end

	    default:
	      do_cmd <= 0 ;
	  endcase

	// rcv state machine...
	// Look for "X" or "x" as SOF.  count bytes 0-27:
	//  0: SOF: x/X
	//  1: type
	//  2,3: CRC
	//  4-11: address, 4 bites per byte
	//  12-27: data, 4 bites per byte

	if (uart_drdy) begin
	   if (((uart_datain == 8'h58) || 
		(uart_datain == 8'h78)) && ~do_cmd) begin
	      if ((rx_state != 0) && echochar) begin
		 txline <= {uart_datain, 8'h0a, 8'h0d} ;
		 val_line <= 3 ;
	      end
	      else if (echochar) begin
		 val_line <= 1 ;
		 txline <= uart_datain ;
	      end
	      rx_state <= 1 ;
	   end
	   else if (rx_state == 27) begin
	      txline <= {8'h0a, 8'h0d, uart_datain} ;
	      rx_state <= 0 ;
	      do_cmd <= 1 ;
	      if (echochar) 
		val_line <= 3 ;
	   end
	   else if (rx_state > 0) begin
	      if ((uart_datain != 8'h5f) && (uart_datain != 8'h20))
		// (allowing '_' and space as optional extra chars
		rx_state <= rx_state + 1 ;
	      txline <= uart_datain ;
	      if (echochar) 
		val_line <= 1 ;
	   end
	   case (rx_state)
	     1: rxtype <= uart_datain ;
	     2: rxcrc[7:4] <= datain_nibble ;
	     3: rxcrc[3:0] <= datain_nibble ;
	     4: rxaddress[31:28] <= datain_nibble ;
	     5: rxaddress[27:24] <= datain_nibble ;
	     6: rxaddress[23:20] <= datain_nibble ;
	     7: rxaddress[19:16] <= datain_nibble ;
	     8: rxaddress[15:12] <= datain_nibble ;
	     9: rxaddress[11:8] <= datain_nibble ;
	     10: rxaddress[7:4] <= datain_nibble ;
	     11: rxaddress[3:0] <= datain_nibble ;
	     12: rxdata[63:60] <= datain_nibble ;
	     13: rxdata[59:56] <= datain_nibble ;
	     14: rxdata[55:52] <= datain_nibble ;
	     15: rxdata[51:48] <= datain_nibble ;
	     16: rxdata[47:44] <= datain_nibble ;
	     17: rxdata[43:40] <= datain_nibble ;
	     18: rxdata[39:36] <= datain_nibble ;
	     19: rxdata[35:32] <= datain_nibble ;
	     20: rxdata[31:28] <= datain_nibble ;
	     21: rxdata[27:24] <= datain_nibble ;
	     22: rxdata[23:20] <= datain_nibble ;
	     23: rxdata[19:16] <= datain_nibble ;
	     24: rxdata[15:12] <= datain_nibble ;
	     25: rxdata[11:8] <= datain_nibble ;
	     26: rxdata[7:4] <= datain_nibble ;
	     27: rxdata[3:0] <= datain_nibble ;
	   endcase
	end
	rxdQ <= rxd ;
	crcsupported <= 0 ;
     end

   wire [127:0] line = {rxtype, rxaddress, rxdata, 
			16'hcbaf, datain_nibble, uart_datain };

   cbaf_uart_rxunit rxunit (
		  .ferr			(ferr),
		  .oerr			(oerr),
		  .drdy			(uart_drdy),
		  .datain		(uart_datain),
		  // Inputs
		  .clk			(clk33),
		  .reset		(resetl),
		  .enable		(uart_rxenable),
		  .rxd			(rxdQ),
		  .rd			(uart_drdy)); 
   
   cbaf_uart_txunit txunit (
		  .txd			(txd),
		  .trege		(uart_tx_rege),
		  .tbufe		(uart_tx_bufe),
		  .clk			(clk33),
		  .reset		(resetl),
		  .enable		(uart_txenable),
		  .load			(uart_load),
		  .datao		(uart_txdata));

endmodule
