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

`undef USE_TMDSROM

`ifdef USE_TMDSROM
module tmdsenc
  (
   input	rst_n,
   input	clk,
   input	den, // It is a data word, not a control word
   input [7:0]	d, // Data word
   input [1:0]	c, // Control word
   output [9:0] q
   );

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

   wire [15:0]		     romq;

   tmdsrom tmdsrom (
		    .clk ( clk ),
		    .d ( d ),
		    .q ( romq )
		    );

   // Delay two cycles to match tmdsrom
   reg [1:0]		     denreg;
   reg [1:0]		     creg[2];

   wire invert =
	(disparity > 4'sd0 & romq[10]) |
	(disparity < 4'sd0 & romq[11]);

   wire cp =  creg[1][0];
   wire cn = ~creg[1][0];
   wire cx = ~creg[1][0] ^ creg[1][1]; // XNOR of C1 and C0

   always @(negedge rst_n or posedge clk)
     if (~rst_n)
       begin
	  disparity <= 4'sd0;
	  denreg    <= 2'b00;
	  creg[0]   <= 2'b00;
	  creg[1]   <= 2'b00;
       end
     else
       begin
	  denreg <= { denreg[0], den };
	  creg[0] <= c;
	  creg[1] <= creg[0];

	  if (denreg[1])
	    begin
	       qreg <= romq[9:0] ^ (invert ? 10'h2ff : 10'h000);
	       disparity <= invert ?
			    disparity - romq[15:12] :
			    disparity + romq[15:12];
	    end
	  else
	    begin
	       qreg <= { cx, {4{cn, cp}}, cp };
	       disparity <= 4'd0;
	    end // else: !if(den)
       end // else: !if(~rst_n)
endmodule // tmdsenc

`else // not USE_TMDSROM

module tmdsenc
  (
   input	rst_n,
   input	clk,
   input	den, // It is a data word, not a control word
   input [7:0]	d, // Data word
   input [1:0]	c, // Control word
   output [9:0] q
   );

   reg signed [3:0] disparity; // Running disparity/2
   reg [9:0]	    qreg = 10'b11010101000; // Symbol C0
   assign q = qreg;

   reg [7:0]	    dreg;
   reg		    denreg;
   reg [1:0]	    creg;

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

   reg [8:0]	     dx;		// X(N)OR stage output
   always @(*)
     begin
	dx[8] = (delta > 3'sd0) | (~&delta & ~dreg[0]);
	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 @ (*)

   wire cp =  creg[0];
   wire cn = ~creg[0];
   wire cx = ~creg[0] ^ creg[1];	// XNOR of c[1] and c[0]

   always @(negedge rst_n or posedge clk)
     if (~rst_n)
       begin
	  disparity <= 4'sd0;
	  qreg      <= 10'b11010101000;
	  dreg      <= 8'hxx;
	  denreg    <= 1'b0;
	  creg      <= 2'b00;
       end
     else
       begin
	  denreg <= den;
	  creg   <= c;
	  dreg   <= d;

	  if (denreg)
	    begin
	       if ( (disparity == 4'sd0) | (delta == 3'sd0) )
		 begin
		    qreg <= { ~dx[8], dx[8], dx[7:0] ^ ~{8{dx[8]}} };
	            disparity <= dx[8] ?
			    disparity + delta :
			    disparity - delta;
	         end
	       else if ( disparity[3] ^ ~delta[2] )
		 begin
		    qreg <= { 1'b1, dx[8], ~dx[7:0] };
		    disparity <= disparity - (delta - dx[8]);
		 end
	       else
		 begin
		    qreg <= { 1'b0, dx[8], dx[7:0] };
		    disparity <= disparity + (delta - ~dx[8]);
		 end
	    end // if (den)
	  else
	    begin
	       qreg <= { cx, {4{cn, cp}}, cp };
	       disparity <= 4'sd0;
	    end // else: !if(den)
       end // else: !if(~rst_n)
endmodule // tmdsenc
`endif
