`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 	Nicolae Dumitrache
// 
// Create Date:    17:40:52 10/28/2015 
// Design Name:    DVI OUT interface
// Module Name:    dvi 
// Project Name: 
// Target Devices: ECP5U
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////

module DVI_OUT
	(
		input clk,		
		input clk5x,	// clk*5
		input [7:0]R,
		input [7:0]G,
		input [7:0]B,
		input hsync,
		input vsync,
		input de,
		output [3:0]TMDS
	);
	
	wire [3:0]tmdsint;
	wire [9:0]tmds_r;
	wire [9:0]tmds_g;
	wire [9:0]tmds_b;
	wire [7:0]itmds;

	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)
	);

	c40to8 c40to8_inst(
		.clk(clk),
		.clk5x(clk5x),
		.din({10'b1111100000, tmds_r, tmds_g, tmds_b}),
		.dout(itmds)
	);

	ioip ioip_inst(
		.refclk(clk5x),
		.reset(1'b0),
		.data(itmds),
		.clkout(),
		.dout(TMDS)
	);

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 c40to8(
	input clk,
	input clk5x,	// clk*5
	input [39:0]din,
	output [7:0]dout
	);
	
	reg start = 1'b0;
	reg [39:0]mem;
	reg [2:0]state = 3'b000;
	assign dout = {mem[31], mem[21], mem[11], mem[1], mem[30], mem[20], mem[10], mem[0]};
	
	always @(negedge clk) start <= 1'b1;
	
	always @(posedge clk5x) if(start) begin
		state <= state + (state[2] ? 3'b100 : 1'b1);
		mem <= |state ? (mem >> 2) : din;
	end
	
endmodule

