//////////////////////////////////////////////////////////////////////////////
//
//  Xilinx, Inc. 2007                 www.xilinx.com
//
//  XAPP460
//
//////////////////////////////////////////////////////////////////////////////
//
//  File name :       hdmi_encoder.v
//
//  Description :     hdmi_encoder 
//
//  Date - revision : March 2008 - 1.0.0
//
//  Author :          Bob Feng
//
//  Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are
//              provided to you "as is". Xilinx and its licensors makeand you
//              receive no warranties or conditions, express, implied,
//              statutory or otherwise, and Xilinx specificallydisclaims any
//              implied warranties of merchantability, non-infringement,or
//              fitness for a particular purpose. Xilinx does notwarrant that
//              the functions contained in these designs will meet your
//              requirements, or that the operation of these designswill be
//              uninterrupted or error free, or that defects in theDesigns
//              will be corrected. Furthermore, Xilinx does not warrantor
//              make any representations regarding use or the results ofthe
//              use of the designs in terms of correctness, accuracy,
//              reliability, or otherwise.
//
//              LIMITATION OF LIABILITY. In no event will Xilinx or its
//              licensors be liable for any loss of data, lost profits,cost
//              or procurement of substitute goods or services, or forany
//              special, incidental, consequential, or indirect damages
//              arising from the use or operation of the designs or
//              accompanying documentation, however caused and on anytheory
//              of liability. This limitation will apply even if Xilinx
//              has been advised of the possibility of such damage. This
//              limitation shall apply not-withstanding the failure ofthe
//              essential purpose of any limited remedies herein.
//
//  Copyright © 2004 Xilinx, Inc.
//  All rights reserved
//
//////////////////////////////////////////////////////////////////////////////
`timescale 1 ps / 1ps

module hdmi_encoder # (
	parameter SDATAINVERT = "FALSE" //Invert SDATA before serilize it
)(
	input			clkin,				// system clock
	input			clkx5in,			// system clock x5
	input			clkx5notin,			// system clock x5 not
	input			rstin,				// reset
	input	[7:0]	blue_din,			// Blue data in
	input	[7:0]	green_din,			// Green data in
	input	[7:0]	red_din,			// Red data in
	input	[3:0]	aux0_din,
	input	[3:0]	aux1_din,
	input	[3:0]	aux2_din,
	input			hsync,				// hsync data
	input			vsync,				// vsync data
	input			vde,				// video data enable
	input			ade,				// audio/aux data enable
	output	[7:0]	tmds_data			// data outputs to ddr in IOB, bit 3(and 7) is clock
);	

	wire 	[9:0]   red;
	wire 	[9:0]   green;
	wire 	[9:0]   blue;
	wire 	[29:0]	s_data ;

	reg  ctl0, ctl1, ctl2, ctl3;

	localparam DILNDPREAM = 4'b1010;
	localparam VIDEOPREAM = 4'b1000;
	localparam NULLCONTRL = 4'b0000;

	always @ (posedge clkin or posedge rstin)
	begin
		if (rstin==1'b1)
			{ctl0, ctl1, ctl2, ctl3} <= 4'b0000;
		else
			{ctl0, ctl1, ctl2, ctl3} <= 
				({vde,ade}==2'b00) ? NULLCONTRL :
				({vde,ade}==2'b10) ? VIDEOPREAM :
				({vde,ade}==2'b11) ? VIDEOPREAM :
				({vde,ade}==2'b01) ? DILNDPREAM :
				NULLCONTRL;
	end

	wire [7:0] blue_dly, green_dly, red_dly;
	wire [3:0] aux0_dly, aux1_dly, aux2_dly;
	wire       hsync_dly, vsync_dly, vde_dly, ade_dly;

srldelay # (
	.WIDTH(40),
	.TAPS(4'b1010)
) srldly_0 (
	.data_i({blue_din, green_din, red_din, aux0_din, aux1_din, aux2_din, hsync, vsync, vde, ade}),
	.data_o({blue_dly, green_dly, red_dly, aux0_dly, aux1_dly, aux2_dly, hsync_dly, vsync_dly, vde_dly, ade_dly}),
	.clk(clkin)
);

hdmi_encode # (
	.CHANNEL("BLUE")
)encb (
	.clkin	(clkin),
	.rstin	(rstin),
	.vdin		(blue_dly),
	.adin   (aux0_dly),
	.c0     (hsync_dly),
	.c1     (vsync_dly),
	.vde		(vde_dly),
	.ade		(ade_dly),
	.dout		(blue)) ;

hdmi_encode # (
	.CHANNEL("GREEN")
) encg (
	.clkin	(clkin),
	.rstin	(rstin),
	.vdin		(green_dly),
	.adin   (aux1_dly),
	.c0     (ctl0),
	.c1     (ctl1),
	.vde		(vde_dly),
	.ade		(ade_dly),
	.dout		(green)) ;

hdmi_encode # (
	.CHANNEL("RED")
) encr (
	.clkin	(clkin),
	.rstin	(rstin),
	.vdin		(red_dly),
	.adin   (aux2_dly),
	.c0     (ctl2),
	.c1     (ctl3),
	.vde		(vde_dly),
	.ade		(ade_dly),
	.dout		(red)) ;

	assign s_data = {red[9], green[9], blue[9], red[8], green[8], blue[8],
	   	 red[7], green[7], blue[7], red[6], green[6], blue[6],
	   	 red[5], green[5], blue[5], red[4], green[4], blue[4],
	   	 red[3], green[3], blue[3], red[2], green[2], blue[2],
	   	 red[1], green[1], blue[1], red[0], green[0], blue[0]} ;

	reg [29:0] s_data_q;

	always @ (posedge clkin or posedge rstin)
	begin
		if (rstin==1'b1)
			s_data_q <= 30'b0;
		else
			if(SDATAINVERT == "TRUE")
				s_data_q <= ~s_data;
			else
				s_data_q <= s_data;
	end

	reg		[9:0] enc0_out_r;
	reg		[9:0] enc1_out_r;
	reg		[9:0] enc2_out_r;

	wire	[9:0] enc0_out_w;
	wire	[9:0] enc1_out_w;
	wire	[9:0] enc2_out_w;

	always @(posedge clkin or posedge rstin)
	begin
		if (rstin==1'b1)
			begin
				enc0_out_r[9:0] <= 10'b0;
				enc1_out_r[9:0] <= 10'b0;
				enc2_out_r[9:0] <= 10'b0;
			end
		else
			begin
				enc0_out_r[9:0] <= enc0_out_w[9:0];
				enc1_out_r[9:0] <= enc1_out_w[9:0];
				enc2_out_r[9:0] <= enc2_out_w[9:0];
			end
	end

	assign enc0_out_w[9:0]=blue[9:0];
	assign enc1_out_w[9:0]=green[9:0];
	assign enc2_out_w[9:0]=red[9:0];


	wire	tx_req;

	reg		[3:0] tx_req_r;
	wire	[3:0] tx_req_w;

	reg		[1:0] tx0_out_r;
	reg		[1:0] tx1_out_r;
	reg		[1:0] tx2_out_r;
	reg		[1:0] tx3_out_r;

	wire	[1:0] tx0_out_w;
	wire	[1:0] tx1_out_w;
	wire	[1:0] tx2_out_w;
	wire	[1:0] tx3_out_w;


	reg		[9:0] tx0_r;
	reg		[9:0] tx1_r;
	reg		[9:0] tx2_r;
	reg		[9:0] tx3_r;

	wire	[9:0] tx0_w;
	wire	[9:0] tx1_w;
	wire	[9:0] tx2_w;
	wire	[9:0] tx3_w;


	assign tmds_data[0]=tx0_out_r[1];
	assign tmds_data[1]=tx1_out_r[1];
	assign tmds_data[2]=tx2_out_r[1];
	assign tmds_data[3]=tx3_out_r[1];
	assign tmds_data[4]=tx0_out_r[0];
	assign tmds_data[5]=tx1_out_r[0];
	assign tmds_data[6]=tx2_out_r[0];
	assign tmds_data[7]=tx3_out_r[0];

	assign tx_req=tx_req_r[3];

	always @(posedge clkx5in or posedge rstin)
	begin
		if (rstin==1'b1)
			begin
				tx_req_r[3:0] <= 4'b0;
				tx0_r[9:0] <= 10'b0;
				tx1_r[9:0] <= 10'b0;
				tx2_r[9:0] <= 10'b0;
				tx3_r[9:0] <= 10'b0;
				tx0_out_r[1:0] <= 2'b0;
				tx1_out_r[1:0] <= 2'b0;
				tx2_out_r[1:0] <= 2'b0;
				tx3_out_r[1:0] <= 2'b0;
			end
		else
			begin
				tx_req_r[3:0] <= tx_req_w[3:0];
				tx0_r[9:0] <= tx0_w[9:0];
				tx1_r[9:0] <= tx1_w[9:0];
				tx2_r[9:0] <= tx2_w[9:0];
				tx3_r[9:0] <= tx3_w[9:0];
				tx0_out_r[1:0] <= tx0_out_w[1:0];
				tx1_out_r[1:0] <= tx1_out_w[1:0];
				tx2_out_r[1:0] <= tx2_out_w[1:0];
				tx3_out_r[1:0] <= tx3_out_w[1:0];
			end
	end

	assign tx_req_w[0]=clkin;
	assign tx_req_w[1]=tx_req_r[0];
	assign tx_req_w[2]=tx_req_r[1];
	assign tx_req_w[3]=(tx_req_r[2:1]==2'b01) ? 1'b1 : 1'b0;

	assign tx0_w[9:0]=(tx_req==1'b1) ? enc0_out_r[9:0] : {2'b00,tx0_r[9:2]};
	assign tx1_w[9:0]=(tx_req==1'b1) ? enc1_out_r[9:0] : {2'b00,tx1_r[9:2]};
	assign tx2_w[9:0]=(tx_req==1'b1) ? enc2_out_r[9:0] : {2'b00,tx2_r[9:2]};
	assign tx3_w[9:0]=(tx_req==1'b1) ? 10'b0000011111 : {2'b00,tx3_r[9:2]};

	assign tx0_out_w[1:0]=tx0_r[1:0];
	assign tx1_out_w[1:0]=tx1_r[1:0];
	assign tx2_out_w[1:0]=tx2_r[1:0];
	assign tx3_out_w[1:0]=tx3_r[1:0];

endmodule
