aboutsummaryrefslogtreecommitdiffstats
path: root/common/timing_detector.v
diff options
context:
space:
mode:
Diffstat (limited to 'common/timing_detector.v')
-rwxr-xr-xcommon/timing_detector.v390
1 files changed, 390 insertions, 0 deletions
diff --git a/common/timing_detector.v b/common/timing_detector.v
new file mode 100755
index 0000000..bb4e872
--- /dev/null
+++ b/common/timing_detector.v
@@ -0,0 +1,390 @@
+//////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2011, Andrew "bunnie" Huang
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation and/or
+// other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+//////////////////////////////////////////////////////////////////////////////
+`timescale 1 ns / 1ps
+
+module timing_detector (
+ input wire pclk,
+ input wire rstin, // asynchronous, as it is asserted during no-clock condition
+ input wire vsync, // assume positive-polarity sync on input
+ input wire hsync, // assume positive-polarity sync on input
+ input wire de,
+ input wire refclk,
+ input wire lcd_de,
+ input wire lcd_vsync,
+
+ output reg [11:0] hactive, // in pixels
+ output reg [11:0] vactive, // in lines
+ output reg [11:0] htotal, // in pixels
+ output reg [23:0] vtotal, // ** in PIXELS ** must divide by htotal in software
+ output reg [7:0] h_frontporch, // in pixels
+ output reg [7:0] h_backporch, // in pixels
+ output reg [23:0] v_frontporch, // ** in PIXELS **
+ output reg [23:0] v_backporch, // ** in PIXELS **
+ output reg [7:0] hsync_width, // in pixels
+ output reg [23:0] vsync_width, // ** in PIXELS **
+ output reg [11:0] lcd_de_latency, // in lines
+ output reg [11:0] lcd_vsync_latency, // in lines
+
+ output reg [23:0] refclkcnt // number of refclocks in a field
+ );
+
+ reg vsync_d;
+ reg hsync_d;
+ reg de_d;
+
+ wire vsync_rising;
+ wire hsync_rising;
+ wire de_rising;
+
+ wire vsync_falling;
+ wire hsync_falling;
+ wire de_falling;
+
+ reg [11:0] hcount;
+ reg [7:0] hsync_width_cnt;
+ reg [23:0] vcount;
+ reg [11:0] de_count;
+ reg [11:0] vactive_count;
+ reg [7:0] h_fp_count;
+ reg [7:0] h_bp_count;
+ reg [23:0] v_fp_count;
+ reg [23:0] v_bp_count;
+
+ reg vsync_refclk;
+ reg vsync__refclk;
+ reg vsync___refclk;
+ reg vsync____refclk;
+ wire vsync_refclk_rising;
+ reg [23:0] refclkcounter;
+
+ reg lcd_de_pclk;
+ reg lcd_de_1pclk;
+ reg lcd_de_2pclk;
+
+ reg lcd_vsync_pclk;
+ reg lcd_vsync_1pclk;
+ reg lcd_vsync_2pclk;
+
+ reg first_de_rising;
+ reg first_lcd_de_rising;
+
+ //// vertical front/back porch machine
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ v_fp_count <= 0;
+ v_bp_count <= 0;
+ v_frontporch <= 0;
+ v_backporch <= 0;
+ first_de_rising <= 1;
+ end else begin
+ if( vsync_falling ) begin
+ v_fp_count <= 0;
+ first_de_rising <= 1;
+ end else begin
+ if( v_fp_count == 24'hFF_FFFF ) begin
+ v_fp_count <= v_fp_count;
+ end else begin
+ v_fp_count <= v_fp_count + 24'b1; // counting in pixels
+ end
+
+ if( de_rising ) begin
+ first_de_rising <= 0;
+ end else begin
+ first_de_rising <= first_de_rising;
+ end
+ end
+
+ if( de_rising && first_de_rising ) begin
+ v_frontporch <= v_fp_count;
+ end else begin
+ v_frontporch <= v_frontporch;
+ end
+
+ if( de_falling ) begin
+ v_bp_count <= 0;
+ end else begin
+ if( v_bp_count == 24'hFF_FFFF ) begin
+ v_bp_count <= v_bp_count;
+ end else begin
+ v_bp_count <= v_bp_count + 24'b1; // counting in pixels
+ end
+ end
+
+ if( vsync_rising ) begin
+ v_backporch <= v_bp_count;
+ end else begin
+ v_backporch <= v_backporch;
+ end
+ end // else: !if( rstin )
+ end // always @ (posedge pclk)
+
+ //// horizonal front/back porch machine
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ h_fp_count <= 0;
+ h_bp_count <= 0;
+ h_frontporch <= 0;
+ h_backporch <= 0;
+ end else begin
+ if( hsync_falling ) begin
+ h_fp_count <= 8'b0;
+ end else begin
+ if( h_fp_count == 8'b1111_1111 ) begin
+ h_fp_count <= h_fp_count; // saturate to catch de-only timings
+ end else begin
+ h_fp_count <= h_fp_count + 8'b1;
+ end
+ end
+
+ if( de_rising ) begin
+ h_frontporch <= h_fp_count + 8'b10; // this is a bit of a hack, why is one pixel missing?
+ end else begin
+ h_frontporch <= h_frontporch;
+ end
+
+ if( de_falling ) begin
+ h_bp_count <= 0;
+ end else begin
+ if( h_bp_count == 8'b1111_1111 ) begin
+ h_bp_count <= h_bp_count;
+ end else begin
+ h_bp_count <= h_bp_count + 8'b1;
+ end
+ end
+
+ if( hsync_rising ) begin
+ h_backporch <= h_bp_count;
+ end else begin
+ h_backporch <= h_backporch;
+ end
+ end // else: !if( rstin )
+ end // always @ (posedge pclk)
+
+ //// vsync_width machine
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ vsync_width <= 0;
+ end else begin
+ if( vsync_rising ) begin
+ vsync_width <= vsync_width;
+ // vcount is reset on vsync_rising as well
+ end else begin
+ if( vsync_falling ) begin
+ vsync_width[23:0] <= vcount[23:0]; // counting in pixels
+ end else begin
+ vsync_width <= vsync_width;
+ end
+ end
+ end
+ end // always @ (posedge pclk)
+
+ //// hsync_width machine
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ hsync_width <= 0;
+ hsync_width_cnt <= 8'b0;
+ end else begin
+ if( hsync_rising ) begin
+ hsync_width <= hsync_width;
+ hsync_width_cnt <= 8'b1;
+ end else begin
+ if( hsync_falling ) begin
+ hsync_width <= hsync_width_cnt;
+ end else begin
+ hsync_width <= hsync_width;
+ end
+ hsync_width_cnt <= hsync_width_cnt + 8'b1;
+ end
+ end
+ end // always @ (posedge pclk)
+
+ //// vactive machine
+ //// add detectors for lcd_de_latency and lcd_vsync_latency
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ vactive <= 0;
+ vactive_count <= 0;
+
+ lcd_de_latency <= 0;
+ lcd_vsync_latency <= 0;
+
+ lcd_de_pclk <= 0;
+ lcd_de_1pclk <= 0;
+ lcd_de_2pclk <= 0;
+
+ lcd_vsync_pclk <= 0;
+ lcd_vsync_1pclk <= 0;
+ lcd_vsync_2pclk <= 0;
+
+ first_lcd_de_rising <= 1;
+ end else begin // if ( rstin )
+ if( vsync_rising ) begin
+ vactive <= vactive_count;
+ vactive_count <= 0;
+ end else begin
+ if( de_rising ) begin // counting in lines
+ vactive_count <= vactive_count + 12'b1;
+ end else begin
+ vactive_count <= vactive_count;
+ end
+ vactive <= vactive;
+ end // else: !if( vsync_rising )
+
+ lcd_de_2pclk <= lcd_de;
+ lcd_de_1pclk <= lcd_de_2pclk;
+ lcd_de_pclk <= lcd_de_1pclk;
+
+ lcd_vsync_2pclk <= lcd_vsync;
+ lcd_vsync_1pclk <= lcd_vsync_2pclk;
+ lcd_vsync_pclk <= lcd_vsync_1pclk;
+
+ if( vsync_rising ) begin
+ first_lcd_de_rising <= 1;
+ end else begin
+ if( !lcd_de_pclk & lcd_de_1pclk ) begin // rising edge
+ first_lcd_de_rising <= 0;
+ end else begin
+ first_lcd_de_rising <= first_lcd_de_rising;
+ end
+ end
+
+ // look for the rising edge
+ if( !lcd_de_pclk & lcd_de_1pclk & first_lcd_de_rising ) begin
+ lcd_de_latency <= vactive_count;
+ end else begin
+ lcd_de_latency <= lcd_de_latency;
+ end
+
+ // look for the rising edge
+ if( !lcd_vsync_pclk & lcd_vsync_1pclk ) begin
+ lcd_vsync_latency <= vactive_count;
+ end else begin
+ lcd_vsync_latency <= lcd_vsync_latency;
+ end
+ end // else: !if( rstin )
+ end // always @ (posedge pclk)
+
+ //// hactive mcahine
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ de_count <= 0;
+ hactive <= 0;
+ end else begin
+ if( de_rising ) begin
+ de_count <= 12'b1; // first pixel counts
+ end else if( de ) begin
+ de_count <= de_count + 12'b1;
+ end else begin
+ de_count <= de_count;
+ end
+
+ if( de_falling ) begin
+ hactive <= de_count;
+ end else begin
+ hactive <= hactive;
+ end
+ end // else: !if( rstin )
+ end // always @ (posedge pclk)
+
+ //// htotal, vtotal machine
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ hcount <= 0;
+ vcount <= 0;
+
+ htotal <= 0;
+ vtotal <= 0;
+ end else begin
+ if( vsync_rising ) begin
+ vtotal <= vcount;
+ vcount <= 24'b1;
+ end else begin
+ vcount <= vcount + 24'b1; /// counting in pixels
+ vtotal <= vtotal;
+ end
+
+ if( de_rising ) begin
+ htotal <= hcount;
+ hcount <= 12'b1;
+ end else begin
+ hcount <= hcount + 12'b1;
+ htotal <= htotal;
+ end
+ end // else: !if( rstin )
+ end // always @ (posedge pclk)
+
+ //// refclock machine
+ //// lots of cross-domain madness that might break things.
+ always @(posedge refclk or posedge rstin) begin
+ if( rstin ) begin
+ refclkcnt <= 0;
+ refclkcounter <= 0;
+
+ vsync____refclk <= 0;
+ vsync___refclk <= 0;
+ vsync__refclk <= 0;
+ vsync_refclk <= 0;
+ end else begin
+ vsync_refclk <= vsync__refclk;
+ vsync__refclk <= vsync___refclk;
+ vsync___refclk <= vsync____refclk;
+ vsync____refclk <= vsync;
+
+ if( vsync_refclk_rising ) begin
+ refclkcnt <= refclkcounter;
+ refclkcounter <= 24'b1;
+ end else begin
+ refclkcnt <= refclkcnt;
+ refclkcounter <= refclkcounter + 24'b01;
+ end
+
+ end // else: !if( rstin )
+ end // always @ (posedge refclk or posedge rstin)
+ assign vsync_refclk_rising = !vsync_refclk & vsync__refclk;
+
+
+ //// rising/falling edge extraction machine
+ always @(posedge pclk or posedge rstin) begin
+ if(rstin) begin
+ vsync_d <= 0;
+ hsync_d <= 0;
+ de_d <= 0;
+ end else begin
+ vsync_d <= vsync;
+ hsync_d <= hsync;
+ de_d <= de;
+ end
+ end // always @ (posedge pclk)
+
+ assign vsync_rising = vsync & !vsync_d;
+ assign hsync_rising = hsync & !hsync_d;
+ assign de_rising = de & !de_d;
+
+ assign vsync_falling = !vsync & vsync_d;
+ assign hsync_falling = !hsync & hsync_d;
+ assign de_falling = !de & de_d;
+
+endmodule // timing_detector