//
//  video freeze with sync
//  (C) Alexey Melnikov
//
//
//  This program is free software; you can redistribute it and/or modify it
//  under the terms of the GNU General Public License as published by the Free
//  Software Foundation; either version 2 of the License, or (at your option)
//  any later version.
//
//  This program is distributed in the hope that it will be useful, but WITHOUT
//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
//  more details.
//
//  You should have received a copy of the GNU General Public License along
//  with this program; if not, write to the Free Software Foundation, Inc.,
//  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.


module video_freezer
(
	input  clk,

	output sync,
	input  freeze,

	input  hs_in,
	input  vs_in,
	input  hbl_in,
	input  vbl_in,

	output hs_out,
	output vs_out,
	output hbl_out,
	output vbl_out
);

sync_lock #(33) vs_lock
(
	.clk(clk),
	.sync_in(vs_in),
	.sync_out(vs_out),
	.de_in(vbl_in),
	.de_out(vbl_out),
	.freeze(freeze)
);

wire sync_pt;
sync_lock #(21) hs_lock
(
	.clk(clk),
	.sync_in(hs_in),
	.sync_out(hs_out),
	.de_in(hbl_in),
	.de_out(hbl_out),
	.freeze(freeze),
	.sync_pt(sync_pt)
);

reg sync_o;
always @(posedge clk) begin
	reg old_hs, old_vs;
	reg vs_sync;

	old_vs <= vs_out;
	
	if(~old_vs & vs_out) vs_sync <= 1;
	if(sync_pt & vs_sync) begin
		vs_sync <= 0;
		sync_o <= ~sync_o;
	end
end

assign sync = sync_o; 

endmodule


module sync_lock #(parameter WIDTH)
(
	input   clk,

	input   sync_in,
	input   de_in,

	output  sync_out,
	output  de_out,

	input   freeze,
	output  sync_pt,
	output  valid
);

reg [WIDTH-1:0] f_len, s_len, de_start, de_end;
reg sync_valid;

reg old_sync;
always @(posedge clk) old_sync <= sync_in;

always @(posedge clk) begin
	reg [WIDTH-1:0] cnti;
	reg f_valid;
	reg old_de;

	cnti <= cnti + 1'd1;
	if(~old_sync & sync_in) begin
		if(sync_valid) f_len <= cnti;
		f_valid <= 1;
		sync_valid <= f_valid;
		cnti <= 0;
	end

	if(old_sync & ~sync_in & sync_valid) s_len <= cnti;
	
	old_de <= de_in;
	if(~old_de & de_in & sync_valid) de_start <= cnti;
	if(old_de & ~de_in & sync_valid) de_end   <= cnti;

	if(freeze) {f_valid, sync_valid} <= 0;
end

reg sync_o, de_o, sync_o_pre;
always @(posedge clk) begin
	reg [WIDTH-1:0] cnto;

	cnto <= cnto + 1'd1;
	if(old_sync & ~sync_in & sync_valid) cnto <= s_len + 2'd2;
	if(cnto == f_len) cnto <= 0;

	sync_o_pre <= (cnto == (s_len>>1)); // middle in sync
	if(cnto == f_len) sync_o <= 1;
	if(cnto == s_len) sync_o <= 0;
	if(cnto == de_start) de_o <= 1;
	if(cnto == de_end)   de_o <= 0;
end

assign sync_out = freeze ? sync_o : sync_in;
assign valid    = sync_valid;
assign sync_pt  = sync_o_pre;
assign de_out   = freeze ? de_o   : de_in;

endmodule
