//
// Encodes a word in TMDS 8/10 format
//

module tmdsenc
  (
   input	rst_n,
   input	clk,
   input	den,    // Video data enable
   input [7:0]	d,      // Video data word
   input	ten,	// TERC data enable
   input [3:0]	t,      // TERC data
   input [1:0]	c,      // Control symbol
   output [9:0] q
   );

   // Control symbols
   wire [9:0]	csym[0:3];
   assign csym[ 0] = 10'b11010_10100;
   assign csym[ 1] = 10'b00101_01011;
   assign csym[ 2] = 10'b01010_10100;
   assign csym[ 3] = 10'b10101_01011;

   // TERC4 symbols
   wire [9:0]	tsym[0:15];
   assign tsym[ 0] = 10'b10100_11100;
   assign tsym[ 1] = 10'b10011_00011;
   assign tsym[ 2] = 10'b10111_00100;
   assign tsym[ 3] = 10'b10111_00010;
   assign tsym[ 4] = 10'b01011_10001;
   assign tsym[ 5] = 10'b01000_11110;
   assign tsym[ 6] = 10'b01100_01110;
   assign tsym[ 7] = 10'b01001_11100;
   assign tsym[ 8] = 10'b10110_01100;
   assign tsym[ 9] = 10'b01001_11001;
   assign tsym[10] = 10'b01100_11100;
   assign tsym[11] = 10'b10110_00110;
   assign tsym[12] = 10'b10100_01110;
   assign tsym[13] = 10'b10011_10001;
   assign tsym[14] = 10'b01011_00011;
   assign tsym[15] = 10'b10110_00011;

   reg signed [4:0] disparity; // Running disparity/2
   reg [9:0]	    qreg;
   assign q = qreg;

   reg [7:0]	    dreg;
   reg		    denreg;
   reg [3:0]	    treg;
   reg		    tenreg;
   reg [1:0]	    creg;

   wire signed [3:0] ddisp =
	dreg[7] + dreg[6] + dreg[5] + dreg[4] +
	dreg[3] + dreg[2] + dreg[1] + dreg[0] - 'sd4;

   reg [8:0]	     dx;		// X(N)OR stage output
   wire signed [3:0] xdisp =		// Does not include dx[8]!
	dx[7] + dx[6] + dx[5] + dx[4] +
	dx[3] + dx[2] + dx[1] + dx[0] - 'sd4;

   always_comb
     begin
	dx[8] = $signed(ddisp | { 3'd0, ~dreg[0] }) <= 4'sd0;
	dx[0] = dreg[0];
	dx[1] = dx[0] ^ dreg[1] ^ ~dx[8];
	dx[2] = dx[1] ^ dreg[2] ^ ~dx[8];
	dx[3] = dx[2] ^ dreg[3] ^ ~dx[8];
	dx[4] = dx[3] ^ dreg[4] ^ ~dx[8];
	dx[5] = dx[4] ^ dreg[5] ^ ~dx[8];
	dx[6] = dx[5] ^ dreg[6] ^ ~dx[8];
	dx[7] = dx[6] ^ dreg[7] ^ ~dx[8];
     end // always @ (*)

   reg [9:0] dq;		// Disparity stage output

   always_comb
     begin
	dq[9]   = ((disparity == 5'sd0) | (xdisp == 4'sd0))
	  ? ~dx[8] : ( disparity[4] == xdisp[3] );
	dq[8]   = dx[8];
	dq[7:0] = dx[7:0] ^ {{8{dq[9]}}};
     end

   wire signed [3:0] qdisp =
	dq[9] + dq[8] + dq[7] + dq[6] + dq[5] +
	dq[4] + dq[3] + dq[2] + dq[1] + dq[0] - 'sd5;

   always @(negedge rst_n or posedge clk)
     if (~rst_n)
       begin
	  disparity <= 'sd0;
	  qreg      <= csym[0];
	  dreg      <= 8'bx;
	  denreg    <= 1'b0;
	  tenreg    <= 1'b0;
	  treg      <= 4'bx;
	  creg      <= 2'b00;
       end
     else
       begin
	  denreg    <= den;
	  tenreg    <= ten;
	  creg      <= c;
	  treg      <= t;
	  dreg      <= d;

	  if (denreg)
	    begin
	       qreg <= dq;
	       disparity <= disparity + qdisp;
	    end
	  else
	    begin
	       qreg <= tenreg ? tsym[treg] : csym[creg];
	       disparity <= 'sd0;
	    end
       end // else: !if(~rst_n)
endmodule // tmdsenc
