Files
FPGA_LVDS_PROTOCOL_ANALYSER/de_monitor.v
2026-06-10 09:32:26 +02:00

114 lines
4.3 KiB
Verilog

// 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