`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 	Nicolae Dumitrache
// 
// Create Date:    17:40:52 05/14/2015 
// Design Name: 	 HDMI OUT interface
// Module Name:    hdmi 
// Project Name: 
// Target Devices: Spartan6
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////

module DVI_OUT
	(
		input clk,		// bufg, from PLL_BASE
		input clk2x,	// bufg, from PLL_BASE
		input clk10x,	// unbuffered, from PLL_BASE
		input pll_locked, // from PLL_BASE
		input [7:0]R,
		input [7:0]G,
		input [7:0]B,
		input hsync,
		input vsync,
		input de,
		output [3:0]TMDS,
		output [3:0]TMDSB
	);
	
	wire bufpll_lock;
	wire [3:0]tmdsint;
	wire [9:0]tmds_r;
	wire [9:0]tmds_g;
	wire [9:0]tmds_b;
	wire [29:0]tmds_rgb = {tmds_r[9:5], tmds_g[9:5], tmds_b[9:5], tmds_r[4:0], tmds_g[4:0], tmds_b[4:0]};
	wire [14:0]tmds;
	wire tmdsclk;

	TMDS_encoder TMDS_RED(
		.clk(clk),
		.VD(R),
		.CD(2'b00),
		.VDE(de),
		.TMDS(tmds_r)
	);

	TMDS_encoder TMDS_GREEN(
		.clk(clk),
		.VD(G),
		.CD(2'b00),
		.VDE(de),
		.TMDS(tmds_g)
	);
	
	TMDS_encoder TMDS_BLUE(
		.clk(clk),
		.VD(B),
		.CD({vsync, hsync}),
		.VDE(de),
		.TMDS(tmds_b)
	);

	c30to15 c30to15_inst(
		.din(tmds_rgb),
		.clk(clk),
		.dout(tmds),
		.clkout(tmdsclk),
		.clk2x(clk2x),
		.reset(!bufpll_lock)
	);

		
  ioip ioip_inst
  (
    .DATA_OUT_FROM_DEVICE({tmdsclk, tmds[14], tmds[9], tmds[4], tmdsclk, tmds[13], tmds[8], tmds[3], tmdsclk, tmds[12], tmds[7], tmds[2], tmdsclk, tmds[11], tmds[6], tmds[1], tmdsclk, tmds[10], tmds[5], tmds[0]}), //Input pins
    .DATA_OUT_TO_PINS_P(TMDS), //Output pins
    .DATA_OUT_TO_PINS_N(TMDSB), //Output pins

    .CLK_IN(clk10x),        	// Fast clock input from PLL/MMCM
    .CLK_DIV_IN(clk2x),    	// Slow clock input from PLL/MMCM
    .LOCKED_IN(pll_locked), 	//Input pin
    .LOCKED_OUT(bufpll_lock), //Output pin
    .IO_RESET(1'b0)  			//system reset
	);

endmodule


module TMDS_encoder(
		input clk,
		input [7:0]VD,  // video data (red, green or blue)
		input [1:0]CD,  // control data
		input VDE,  	 // video data enable, to choose between CD (when VDE=0) and VD (when VDE=1)
		output reg [9:0]TMDS = 10'b0000000000
	);

	wire [3:0]Nb1s = VD[0] + VD[1] + VD[2] + VD[3] + VD[4] + VD[5] + VD[6] + VD[7];
	wire XNOR = (Nb1s > 4'd4) || (Nb1s == 4'd4 && VD[0] == 1'b0);
	wire [8:0]iq_m = {~XNOR, iq_m[6:0] ^ VD[7:1] ^ {7{XNOR}}, VD[0]};
	reg [8:0]q_m;
	reg rVDE;
	reg [3:0]balance_acc = 0;
	wire [3:0]balance = q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7] - 4'd4;
	wire balance_sign_eq = (balance[3] == balance_acc[3]);
	wire invert_q_m = (balance == 4'b0000 || balance_acc == 4'b0000) ? ~q_m[8] : balance_sign_eq;
	wire [3:0] balance_acc_inc = balance - ({q_m[8] ^ ~balance_sign_eq} & ~(balance == 4'b0000 || balance_acc == 4'b0000));
	wire [3:0] balance_acc_new = invert_q_m ? (balance_acc - balance_acc_inc) : (balance_acc + balance_acc_inc);
	wire [9:0] TMDS_data = {invert_q_m, q_m[8], q_m[7:0] ^ {8{invert_q_m}}};
	wire [9:0] TMDS_code = CD[1] ? (CD[0] ? 10'b1010101011 : 10'b0101010100) : (CD[0] ? 10'b0010101011 : 10'b1101010100);

	always @(posedge clk) begin
		q_m <= iq_m;
		rVDE <= VDE;
		TMDS <= rVDE ? TMDS_data : TMDS_code;
		balance_acc <= rVDE ? balance_acc_new : 4'h0;
	end
	
endmodule


module c30to15 (
		input [29:0]din,
		input clk,
		output reg [14:0]dout,
		output clkout,
		input clk2x,
		input reset
	);

	reg [29:0]mem[1:0];
	reg [1:0]raddr = 2'b00;
	reg ceb = 1'b0;
	reg cea = 1'b0;
	reg waddr = 1'b0;
	assign clkout = !raddr[0];
	
	always @(posedge clk or posedge reset) begin
		if(reset) begin
			cea <= 1'b0;
			waddr <= 1'b0;
		end else begin
			cea <= 1'b1;
			waddr <= !waddr;
			mem[waddr] <= din;
		end
	end

	always @(posedge clk2x) begin
		ceb <= cea;
		if(ceb) begin
			dout <= raddr[0] ? mem[raddr[1]][29:15] : mem[raddr[1]][14:0];
			raddr <= raddr + 1'b1;
		end
	end
		
endmodule
