// // scandoubler.v // // Copyright (c) 2015 Till Harbaum // Copyright (c) 2017 Sorgelig // // This source file 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 3 of the License, or // (at your option) any later version. // // This source file 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, see . // TODO: Delay vsync one line module scandoubler #(parameter LENGTH, parameter HALF_DEPTH) ( // system interface input clk_sys, input ce_pix, input ce_pix_actual, input hq2x, // shifter video interface input hs_in, input vs_in, input line_start, input [DWIDTH:0] r_in, input [DWIDTH:0] g_in, input [DWIDTH:0] b_in, input mono, // output interface output reg hs_out, output vs_out, output [DWIDTH:0] r_out, output [DWIDTH:0] g_out, output [DWIDTH:0] b_out ); localparam DWIDTH = HALF_DEPTH ? 2 : 5; assign vs_out = vs_in; reg [2:0] phase; reg [2:0] ce_div; reg [7:0] pix_len = 0; wire [7:0] pl = pix_len + 1'b1; reg ce_x1, ce_x4; reg req_line_reset; wire ls_in = hs_in | line_start; always @(negedge clk_sys) begin reg old_ce; reg [2:0] ce_cnt; reg [7:0] pixsz2, pixsz4 = 0; old_ce <= ce_pix; if(~&pix_len) pix_len <= pix_len + 1'd1; ce_x4 <= 0; ce_x1 <= 0; // use such odd comparison to place c_x4 evenly if master clock isn't multiple 4. if((pl == pixsz4) || (pl == pixsz2) || (pl == (pixsz2+pixsz4))) begin phase <= phase + 1'd1; ce_x4 <= 1; end if(~old_ce & ce_pix) begin pixsz2 <= {1'b0, pl[7:1]}; pixsz4 <= {2'b00, pl[7:2]}; ce_x1 <= 1; ce_x4 <= 1; pix_len <= 0; phase <= phase + 1'd1; ce_cnt <= ce_cnt + 1'd1; if(ce_pix_actual) begin phase <= 0; ce_div <= ce_cnt + 1'd1; ce_cnt <= 0; req_line_reset <= 0; end if(ls_in) req_line_reset <= 1; end end reg ce_sd; always @(*) begin case(ce_div) 2: ce_sd = !phase[0]; 4: ce_sd = !phase[1:0]; default: ce_sd <= 1; endcase end `define BITS_TO_FIT(N) ( \ N <= 2 ? 0 : \ N <= 4 ? 1 : \ N <= 8 ? 2 : \ N <= 16 ? 3 : \ N <= 32 ? 4 : \ N <= 64 ? 5 : \ N <= 128 ? 6 : \ N <= 256 ? 7 : \ N <= 512 ? 8 : \ N <=1024 ? 9 : 10 ) localparam AWIDTH = `BITS_TO_FIT(LENGTH); Hq2x #(.LENGTH(LENGTH), .HALF_DEPTH(HALF_DEPTH)) Hq2x ( .clk(clk_sys), .ce_x4(ce_x4 & ce_sd), .inputpixel({b_in,g_in,r_in}), .mono(mono), .disable_hq2x(~hq2x), .reset_frame(vs_in), .reset_line(req_line_reset), .read_y(sd_line), .read_x(sd_h_actual), .outpixel({b_out,g_out,r_out}) ); reg [10:0] sd_h_actual; always @(*) begin case(ce_div) 2: sd_h_actual = sd_h[10:1]; 4: sd_h_actual = sd_h[10:2]; default: sd_h_actual = sd_h; endcase end reg [10:0] sd_h; reg [1:0] sd_line; always @(posedge clk_sys) begin reg [11:0] hs_max,hs_rise,hs_ls; reg [10:0] hcnt; reg [11:0] sd_hcnt; reg hs, hs2, vs, ls; if(ce_x1) begin hs <= hs_in; ls <= ls_in; if(ls && !ls_in) hs_ls <= {hcnt,1'b1}; // falling edge of hsync indicates start of line if(hs && !hs_in) begin hs_max <= {hcnt,1'b1}; hcnt <= 0; if(ls && !ls_in) hs_ls <= {10'd0,1'b1}; end else begin hcnt <= hcnt + 1'd1; end // save position of rising edge if(!hs && hs_in) hs_rise <= {hcnt,1'b1}; vs <= vs_in; if(vs && ~vs_in) sd_line <= 0; end if(ce_x4) begin hs2 <= hs_in; // output counter synchronous to input and at twice the rate sd_hcnt <= sd_hcnt + 1'd1; sd_h <= sd_h + 1'd1; if(hs2 && !hs_in) sd_hcnt <= hs_max; if(sd_hcnt == hs_max) sd_hcnt <= 0; // replicate horizontal sync at twice the speed if(sd_hcnt == hs_max) hs_out <= 0; if(sd_hcnt == hs_rise) hs_out <= 1; if(sd_hcnt == hs_ls) sd_h <= 0; if(sd_hcnt == hs_ls) sd_line <= sd_line + 1'd1; end end endmodule