// de_monitor.v // // Monitors DE timing on the pixel-clock side (RxCLKOUT, ~72 MHz). // // - Counts DE rising edges per frame. // - Counts pixel-clock cycles per DE active interval (line width). // - Frame boundary is declared when DE has been idle longer than one // line period (the gap between the last active line and the first // line of the next frame is the vertical blanking interval, which is // many line periods long; any intra-frame gap is shorter than one // line period). // - Flags an anomaly if the just-finished frame had lines != EXPECTED_LINES, // or if any line in the frame had width != EXPECTED_WIDTH. // // Outputs on `frame_done` (1 pix_clk pulse): // lines_o - lines counted in the frame that just ended // width_o - width of the offending line if any width was wrong, // otherwise the width of the last line in the frame // anomaly_o - 1 if lines_o != EXPECTED_LINES or any width was wrong // // Assumes DE is active-high (DS90CF386 default for the Ampire panel). module de_monitor #( parameter [15:0] EXPECTED_LINES = 16'd800, parameter [15:0] EXPECTED_WIDTH = 16'd1280, // Idle-gap threshold for declaring "end of frame". Must be larger // than one full line period in pixel clocks (~1432 for this panel) // and smaller than the vertical blanking interval (~54k pixels). parameter [15:0] FRAME_GAP_PIX = 16'd2048 )( input wire pix_clk, input wire rst_n, input wire de, output reg frame_done, // 1 pix_clk pulse output reg [15:0] lines_o, output reg [15:0] width_o, output reg anomaly_o ); reg de_q; wire de_rise = de & ~de_q; wire de_fall = ~de & de_q; reg [15:0] line_width; // running width of current DE-high interval reg [15:0] last_width; // width of most recent completed line reg [15:0] bad_width; // sticky: width of first wrong-width line in frame reg any_bad_width; // sticky: at least one wrong-width line this frame reg [15:0] line_count; // lines seen so far in current frame reg [15:0] gap_count; // pixel-clocks since DE last fell reg frame_active; // we have seen at least one DE this frame always @(posedge pix_clk or negedge rst_n) begin if (!rst_n) begin de_q <= 1'b0; line_width <= 16'd0; last_width <= 16'd0; bad_width <= 16'd0; any_bad_width <= 1'b0; line_count <= 16'd0; gap_count <= 16'd0; frame_active <= 1'b0; frame_done <= 1'b0; lines_o <= 16'd0; width_o <= 16'd0; anomaly_o <= 1'b0; end else begin de_q <= de; frame_done <= 1'b0; // --- per-line width measurement --- if (de_rise) begin line_width <= 16'd1; end else if (de) begin line_width <= line_width + 16'd1; end if (de_fall) begin last_width <= line_width; if (line_width != EXPECTED_WIDTH && !any_bad_width) begin bad_width <= line_width; any_bad_width <= 1'b1; end end // --- line counting & frame-boundary detection --- if (de_rise) begin line_count <= line_count + 16'd1; frame_active <= 1'b1; gap_count <= 16'd0; end else if (!de) begin if (gap_count != 16'hFFFF) gap_count <= gap_count + 16'd1; // End of frame: long DE-idle after at least one line. if (frame_active && gap_count == FRAME_GAP_PIX) begin frame_done <= 1'b1; lines_o <= line_count; width_o <= any_bad_width ? bad_width : last_width; anomaly_o <= (line_count != EXPECTED_LINES) || any_bad_width; // reset frame state line_count <= 16'd0; gap_count <= 16'd0; any_bad_width <= 1'b0; bad_width <= 16'd0; frame_active <= 1'b0; end end end end endmodule