
//===========================================================================
// RAW(Bayer) to RGB Conversion
//---------------------------------------------------------------------------
//   Pixel Array:
//     R  Gr
//     Gb B
//===========================================================================

module raw2rgb #(

parameter	P_RAW_BW				= 8,
parameter	P_RGB_BW				= 8,
parameter	P_WDH_BW				= 11,
parameter	P_HGT_BW				= 11
)(

input	wire						i_rst_n,
input	wire						i_pclk,
input	wire						i_vsync,
input	wire						i_hsync,
input	wire	[P_RAW_BW  -1: 0]	i_pdat,
input	wire	[P_WDH_BW  -1: 0]	i_width,
input	wire	[P_HGT_BW  -1: 0]	i_height,
input	wire	[P_WDH_BW  -1: 0]	i_hblank,
output	reg							o_vsync,
output	reg							o_hsync,
output	reg		[P_RGB_BW*3-1: 0]	o_pdat
);


localparam	P_LBW_BW				= P_WDH_BW - 1;


//-----------------------------
//  Input Stage DFF
//-----------------------------
reg							hsync_r, vsync_r;
reg		[P_RAW_BW  -1: 0]	pdat_r;

always @( posedge i_pclk or negedge i_rst_n ) begin
	if( !i_rst_n ) begin
		hsync_r		<= 1'b0;
		vsync_r		<= 1'b0;
		pdat_r		<= {P_RAW_BW{1'b0}};
	end
	else begin
		hsync_r		<= i_hsync;
		vsync_r		<= i_vsync;
		pdat_r		<= i_pdat;
	end
end

//-----------------------------
//  State Control
//-----------------------------
wire						rise_vsync			=  i_vsync & ~vsync_r;
wire						fall_vsync			= ~i_vsync &  vsync_r;
wire						rise_hsync			=  i_hsync & ~hsync_r;
wire						fall_hsync			= ~i_hsync &  hsync_r;

reg		[P_WDH_BW-1: 0]		cnt_h_r;
reg		[P_HGT_BW-1: 0]		cnt_v_r;
reg		[P_WDH_BW-1: 0]		cnt_hb_r;

wire						cnt_h_max			= (cnt_h_r == i_width);
wire						cnt_v_max_m2		= (cnt_v_r == i_height);
wire						cnt_v_max_m1		= (cnt_v_r == i_height + 1);
wire						cnt_v_max			= (cnt_v_r == i_height + 2);
wire						cnt_v_add_hb		= cnt_v_max_m2 | cnt_v_max_m1;
wire						cnt_v_add_h			= cnt_v_max_m1 | cnt_v_max;
wire						cnt_hb_max			= (cnt_hb_r == i_hblank);
wire						cnt_hv_max			= cnt_h_max & cnt_v_max;

//--  H Counter
wire						enb_cnt_h_add		= cnt_v_add_h & (cnt_hb_r == {P_WDH_BW{1'b0}});
wire						enb_cnt_h			= i_hsync | enb_cnt_h_add;

always @( posedge i_pclk or negedge i_rst_n ) begin
	if		( !i_rst_n )		cnt_h_r			<= {P_WDH_BW{1'b0}};
	else if	( rise_vsync )		cnt_h_r			<= {P_WDH_BW{1'b0}};
	else if	( cnt_h_max )		cnt_h_r			<= {P_WDH_BW{1'b0}};
	else if	( enb_cnt_h )		cnt_h_r			<= cnt_h_r + {{(P_WDH_BW-1){1'b0}},1'b1};
end

//--  V Counter
wire						cnt_v_add			= cnt_v_add_hb & cnt_hb_max;
wire						enb_cnt_v			= rise_hsync | cnt_v_add;

always @( posedge i_pclk or negedge i_rst_n ) begin
	if		( !i_rst_n )		cnt_v_r			<= {P_HGT_BW{1'b0}};
	else if	( rise_vsync )		cnt_v_r			<= {P_HGT_BW{1'b0}};
	else if	( cnt_hv_max )		cnt_v_r			<= {P_HGT_BW{1'b0}};
	else if	( enb_cnt_v )		cnt_v_r			<= cnt_v_r + {{(P_HGT_BW-1){1'b0}},1'b1};
end

//-- H Blank Counter
wire						cnt_hb_start		= cnt_h_max & cnt_v_add_hb;
wire						enb_cnt_hb			= cnt_hb_start || (cnt_hb_r != {P_WDH_BW{1'b0}});

always @( posedge i_pclk or negedge i_rst_n ) begin
	if		( !i_rst_n )		cnt_hb_r		<= {P_WDH_BW{1'b0}};
	else if	( rise_vsync )		cnt_hb_r		<= {P_WDH_BW{1'b0}};
	else if	( cnt_hb_max )		cnt_hb_r		<= {P_WDH_BW{1'b0}};
	else if	( enb_cnt_hb )		cnt_hb_r		<= cnt_hb_r + {{(P_WDH_BW-1){1'b0}},1'b1};
end

//-- State Machine
reg		[1: 0]				state_r;
`define ST_IDLE				2'h0
`define ST_IN				2'h1				// input          (top 2 line)
`define ST_IN_OUT			2'h2				// input & output
`define ST_OUT				2'h3				//         output (bottom 2 line)
wire						trg_in				= rise_vsync;
wire						trg_in_out			= rise_hsync & (cnt_v_r == 'd2);
wire						trg_out				= cnt_h_max & cnt_v_max_m2;

reg		[2: 0]				hv_max_r;
always @( posedge i_pclk or negedge i_rst_n ) begin
	if		( !i_rst_n )		hv_max_r		<= 3'd0;
	else						hv_max_r		<= {hv_max_r[1:0], cnt_hv_max};
end
wire						trg_end				= hv_max_r[2];

always @( posedge i_pclk or negedge i_rst_n ) begin
	if		( !i_rst_n )		state_r			<= `ST_IDLE;
	else if	( trg_end )			state_r			<= `ST_IDLE;
	else if	( trg_in )			state_r			<= `ST_IN;
	else if	( trg_in_out )		state_r			<= `ST_IN_OUT;
	else if	( trg_out )			state_r			<= `ST_OUT;
end


//-----------------------------
//  Line Buffer
//-----------------------------
wire	[P_RAW_BW*2-1: 0]	buf_rdat0, buf_rdat1;
wire	[P_LBW_BW  -1: 0]	buf_addr0			= cnt_h_r[P_WDH_BW-1: 1];
wire						buf_wren0			= cnt_h_r[0];
wire	[P_RAW_BW*2-1: 0]	buf_wdat0			= ( cnt_v_add_h )?	 buf_rdat0[P_RAW_BW*2-1:       0]
												: ( cnt_v_r[0] )?	{pdat_r   [P_RAW_BW  -1:       0],
																	 buf_rdat0[P_RAW_BW  -1:       0]}
																:	{buf_rdat0[P_RAW_BW*2-1:P_RAW_BW],
																	 pdat_r   [P_RAW_BW  -1:       0]};

reg		[P_LBW_BW  -1: 0]	buf_addr0_r;
reg							buf_wren0_r;

always @( posedge i_pclk or negedge i_rst_n ) begin
	if		( !i_rst_n ) begin
		buf_addr0_r		<= {P_LBW_BW{1'b0}};
		buf_wren0_r		<= 1'b0;
	end
	else begin
		buf_addr0_r		<= buf_addr0;
		buf_wren0_r		<= buf_wren0;
	end
end

wire	[P_LBW_BW  -1: 0]	buf_addr1			= buf_addr0_r;
wire						buf_wren1			= buf_wren0_r;
wire	[P_RAW_BW*2-1: 0]	buf_wdat1			= ( cnt_v_add_h )?	 buf_rdat1[P_RAW_BW*2-1:       0]
												: ( cnt_v_r[0] )?	{pdat_r   [P_RAW_BW  -1:       0],
																	 buf_rdat1[P_RAW_BW  -1:       0]}
																:	{buf_rdat1[P_RAW_BW*2-1:P_RAW_BW],
																	 pdat_r   [P_RAW_BW  -1:       0]};

line_buf	line_buf0 (
	.clock				( i_pclk				),
	.address			( buf_addr0				),
	.data				( buf_wdat0				),
	.wren				( buf_wren0				),
	.q					( buf_rdat0				)
);

line_buf	line_buf1 (
	.clock				( i_pclk				),
	.address			( buf_addr1				),
	.data				( buf_wdat1				),
	.wren				( buf_wren1				),
	.q					( buf_rdat1				)
);

reg		[P_RAW_BW*2-1: 0]	buf_rdat0_r, buf_rdat1_r;

always @( posedge i_pclk or negedge i_rst_n ) begin
	if		( !i_rst_n )		buf_rdat0_r		<= {(P_RAW_BW*2){1'b0}};
	else if	( buf_wren0 )		buf_rdat0_r		<= buf_rdat0;
end

always @( posedge i_pclk or negedge i_rst_n ) begin
	if		( !i_rst_n )		buf_rdat1_r		<= {(P_RAW_BW*2){1'b0}};
	else if	( buf_wren1 )		buf_rdat1_r		<= buf_rdat1;
end

//-----------------------------
//  Output
//-----------------------------
wire						enb_h				= (cnt_h_r != {P_WDH_BW{1'b0}});
reg							enb_h_r;
reg							enb_rep_r;

always @( posedge i_pclk or negedge i_rst_n ) begin
	if( !i_rst_n ) begin
		enb_h_r			<= 1'b0;
		enb_rep_r		<= 1'b0;
	end
	else begin
		enb_h_r			<= enb_h;
		enb_rep_r		<= cnt_h_max;
	end
end

wire						vsync				= state_r[1];
wire						hsync				= state_r[1] & enb_h_r;

//-- RGB Selection
wire	[P_RGB_BW-1: 0]		r_dat				= ( buf_wren1 )?  buf_rdat0_r[P_RGB_BW*2-1:P_RGB_BW]
												               :  buf_rdat0  [P_RGB_BW*2-1:P_RGB_BW];
wire	[P_RGB_BW-1: 0]		b_dat				= ( buf_wren1 )?  buf_rdat1  [P_RGB_BW  -1:       0]
															   :  buf_rdat1_r[P_RGB_BW  -1:       0];
wire	[P_RGB_BW  : 0]		g_add				= ( buf_wren1 )?  buf_rdat0_r[P_RGB_BW  -1:       0]
												                + buf_rdat1  [P_RGB_BW*2-1:P_RGB_BW]
												               :  buf_rdat0  [P_RGB_BW  -1:       0]
																+ buf_rdat1_r[P_RGB_BW*2-1:P_RGB_BW];
wire	[P_RGB_BW-1: 0]		g_dat				= g_add[P_RGB_BW: 1];

//-- Output Stage DFF
always @( posedge i_pclk or negedge i_rst_n ) begin
	if( !i_rst_n ) begin
		o_vsync		<= 1'b0;
		o_hsync		<= 1'b0;
	end
	else begin
		o_vsync		<= vsync;
		o_hsync		<= hsync;
	end
end

always @( posedge i_pclk or negedge i_rst_n ) begin
	if		( !i_rst_n )		o_pdat		<= {(P_RGB_BW*3){1'b0}};
	else if	( enb_rep_r )		o_pdat		<= o_pdat;
	else if	( hsync )			o_pdat		<= {r_dat, g_dat, b_dat};
	else						o_pdat		<= {(P_RGB_BW*3){1'b0}};
end

endmodule
