From 889a5a5395872eeb3740d5d3281b56c7afa47a6f Mon Sep 17 00:00:00 2001 From: bnewbold Date: Fri, 20 Jan 2012 19:14:58 -0500 Subject: initial import from NeTV archive This repository is simply a mirror of the file fpga/hdmi_overlay_0xD_src.tgz downloaded from http://www.kosagi.com/netv_hardware/ on Jan 19th, 2011. It seems to contain all the verilog and scripts required to build FPGA firmware for the NeTV HDMI device from Chumby/Sutajio Ko-Usagi; see http://www.kosagi.com/blog/ for more information. Licensing is vague; see ip/license.txt --- lcd_input_v4.v | 853 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 853 insertions(+) create mode 100755 lcd_input_v4.v (limited to 'lcd_input_v4.v') diff --git a/lcd_input_v4.v b/lcd_input_v4.v new file mode 100755 index 0000000..2ac7a55 --- /dev/null +++ b/lcd_input_v4.v @@ -0,0 +1,853 @@ +////////////////////////////////////////////////////////////////////////////// +// 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. +// + +///////////// +// This module takes in video data via an LCD interface +// and attempts to align it to an ongoing HDMI stream. +// +// This is a reboot of the LCD module, version 3 +// +// In this iteration, we attempt to make a very strong guarantee that: +// -- the incoming HDMI stream is the master source of all timing +// -- that the LCD data is always squared into the HDMI stream, and strictly +// resynchronized every V *and* H period +// -- that at least 8 lines of buffering are available +// -- in practice, I'm seeing about +/- 0.08% difference in clocks. That is +// about +/- one full line of data due to absolute clock skew alone. +// -- I see hiccoughs from the PXA168 on the order of 5-6 lines per frame +// +// Version 4 takes advantage of synchronous clocking and makes a +// feedback-loop controlled system that attempts to use genlock to "kick" +// the host CPU into a closer sync relationship, within 8 lines of leading +// the HDMI display. +// +// The feedback loop *only* works if the host controller has the identical +// timing to the incoming stream. Fortunately, a timing measurement module +// has also been created so that the CPU can read out the timing of the incoming +// stream and adjust the controller to match. +// +///////////// +`timescale 1 ns / 1 ps + +module lcd_input( + input wire rstin, + input wire lcd_dclk, + input wire lcd_de, // active high + input wire lcd_hsync, // always active high + input wire lcd_vsync, // always active high + input wire lcd_sync_pol, // force to 1 at top level module + input wire [5:0] lcd_b, + input wire [5:0] lcd_g, + input wire [5:0] lcd_r, + + input wire hdmi_pclk, + input wire hdmi_de, // active high + input wire hdmi_vsync, // use sync_pol + input wire hdmi_hsync, // use sync_pol + input wire hdmi_sync_pol, + input wire hdmi_cv, // indicates that hdmi sync signals are valid + + output reg [7:0] hdmi_b, + output reg [7:0] hdmi_r, + output reg [7:0] hdmi_g, + + output reg genlock, + output reg locked, + + output wire dummy, + + input wire [2:0] line_full_level, // nominally 2 + input wire [2:0] line_empty_level, // nominally 2 + input wire [3:0] write_init_level, // nominally 1 + input wire [3:0] read_init_level, // nominally 2 + + input wire [23:0] target_lead_pixels, + input wire reset_lock_machine, + + input wire [15:0] lock_tolerance, + input wire smartlock_on + ); + + //////// FIFO connections + wire [(NUMLINES-1) : 0] fifo_rst; + + wire [17:0] fifo_din; + wire [(NUMLINES-1) : 0] fifo_write; + wire [(NUMLINES-1) : 0] fifo_full; + wire [(NUMLINES-1) : 0] fifo_over; + wire lcd_en; + + wire [(NUMLINES-1) : 0] fifo_read; + wire [(NUMLINES-1) : 0] fifo_empty; + wire [(NUMLINES-1) : 0] fifo_under; + wire hdmi_en; + + wire [17:0] fifo_dout0; + wire [17:0] fifo_dout1; + wire [17:0] fifo_dout2; + wire [17:0] fifo_dout3; + wire [17:0] fifo_dout4; + wire [17:0] fifo_dout5; + wire [17:0] fifo_dout6; + wire [17:0] fifo_dout7; + + + /// other connections + ////// HDMI clock domain + reg hdmi_hsync_v; // active when high + reg hdmi_hsync_v2; + wire hdmi_hsync_rising; + + reg hdmi_vsync_v; + reg hdmi_vsync_v2; + wire hdmi_vsync_rising; + wire hdmi_vsync_falling; + + wire lcd_de_rising__hdmi; + wire lcd_de_falling__hdmi; + wire lcd_firstbyte__hdmi; + + reg lcd_de_s__hdmi; + reg lcd_de__hdmi; + reg lcd_de_d__hdmi; + + reg lcd_overflow_s__hdmi; + reg lcd_overflow__hdmi; + + reg lcd_vsync_1hdmi; + reg lcd_vsync_2hdmi; + reg lcd_vsync_hdmi; + wire lcd_vsync_rising__hdmi; + + wire hdmi_underflow; + wire hdmi_empty; + wire [17:0] hdmi_fifo_d; + + reg hdmi_de_d; + wire hdmi_de_rising; + wire hdmi_de_falling; + reg hdmi_first_de; + reg hdmi_first_de_state; + + reg hdmi_read_en; + + ////// LCD clock domain + wire lcd_de_vsync; + reg lcd_de_d; + reg lcd_de_rising; + reg lcd_de_falling; + wire lcd_overflow; + reg lcd_hsync_d; + reg lcd_hsync_rising; + + reg hdmi_vsync_s__lcd; // synchronize hdmi vsync to LCD clock domain + reg hdmi_vsync__lcd; + + reg hdmi_hsync_s__lcd; // synchronize hdmi hsync to LCD clock domain + reg hdmi_hsync__lcd; + + reg hdmi_hsync_rising__lcd; + reg hdmi_vsync_rising__lcd; + reg hdmi_vsync_falling__lcd; + reg hdmi_hsync_d__lcd; + reg hdmi_vsync_d__lcd; + + wire advance_write; + wire advance_read; + + /////////////// + // Synchronous FIFO feedback loop. + // + // Assume: vsync from HDMI drives all timing. + // + // A counter is used to delay the genlock output to the CPU, based on a certain number of + // pixel counts. The feedback loop attempts to tune the delay so that the actual measured + // LCD vsync timing hits a target number of pixels before the HDMI vsync signal. If this + // target is achieved, then fifo read/write timing is simply the LCD DE and HDMI DE signals. + // + /////////////// + parameter GENLOCK_LENGTH = 24'h400; // approx 6 us minimum pulse length, typ 12 us or so + // just needs to be long enough for the CPU to catch the interrupt + + reg [23:0] current_genlock_delay; // delay in pixels of genlock pulse from vsync + reg [23:0] genlock_count; + reg [23:0] measured_vsync_to_vsync; + reg hdmi_vsync_happened; + + reg hdmi_vsync_happened_d; + wire hdmi_vsync_happened_rising; + + wire [23:0] timing_difference; + always @(posedge hdmi_pclk or posedge rstin) begin + if( rstin || reset_lock_machine ) begin + current_genlock_delay <= 32'h40000; // get us a little closer to lock, but not all the way there + // this constant picked to not exceed final length for lowest resolution mode + + genlock_count <= 0; + + measured_vsync_to_vsync <= 0; + hdmi_vsync_happened <= 0; + hdmi_vsync_happened_d <= 0; + locked <= 0; + end else begin // if ( rstin || reset_lock_machine ) + // make a counter, starting at rising edge of vsync, to count time till genlock pulse + if(hdmi_vsync_rising) begin + genlock_count <= 0; + end else begin + genlock_count <= genlock_count + 24'b1; + end + + // generate a genlock pulse when the count is between the current delay target and the + // specified pulse length + if( (genlock_count > current_genlock_delay) && + (genlock_count < current_genlock_delay + GENLOCK_LENGTH) ) begin + // a flaw of this scheme is that we're always hitting genlock. This means we are + // building the jitter of the interrupt latency of the PXA168 into the loop every + // time we hit this. A potentially smarter way to do this is to eventually turn off + // the genlock interrupt and let the PXA168 LCD controller free-wheel once the + // delay is trimmed. This works because the controller and the HDMI stream are + // fully synchronous, so you will get no cumulative slip due to long term frequency + // offset between the two streams. However, a trivial circuit that just gates + // genlock based upon the "lock" bit comupted below will cause, on the next frame, + // vsync to come a full interrupt latency period earlier, which would "unlock" + // the circuit. In order to counter this, we can possibly just open up the lock + // state computation sufficiently wide so as to encompass the interrupt latency + // of the PXA168 -- but this is a little bit tricky in terms of loop dynamics + // so initially, we'll do a naive implementation and see if it's good enough. + if( smartlock_on && !locked ) begin + genlock <= 1'b1; + end else if( smartlock_on && locked ) begin + genlock <= 1'b0; + end else begin + genlock <= 1'b1; + end + end else begin + genlock <= 1'b0; + end + + // this counter measures the time between when the LCD vsync pulse actually rises + // and when the HDMI vsync pulse actually rises. + // It resets on the rising edge of the LCD vsync pulse, and stops counting when + // the hdmi vsync pulse rises + if( lcd_vsync_rising__hdmi ) begin + measured_vsync_to_vsync <= 0; + hdmi_vsync_happened <= 0; + end else begin + if( !hdmi_vsync_happened ) begin + measured_vsync_to_vsync <= measured_vsync_to_vsync + 24'b1; + end else begin + measured_vsync_to_vsync <= measured_vsync_to_vsync; + end + + if( hdmi_vsync_rising ) begin + hdmi_vsync_happened <= 1; + end else begin + hdmi_vsync_happened <= hdmi_vsync_happened; + end + end // else: !if( lcd_vsync_rising__hdmi ) + hdmi_vsync_happened_d <= hdmi_vsync_happened; + + // once the HDMI vsync pulse happens, and the difference counter has stopped, + // we can measure the value. If the difference is too big, take half the difference + // and add it to the current target. The difference is considered in absolute value + // so we need to pay attention to the sign in this logic, and also provide for + // a "zero state" that's fully locked. + if( hdmi_vsync_happened_rising ) begin + if( measured_vsync_to_vsync > target_lead_pixels ) begin + // in this case, LCD vsync is happening too early, so lengthen genlock delay + current_genlock_delay[23:0] <= current_genlock_delay[23:0] + {1'b0,timing_difference[23:1]}; + end else if (measured_vsync_to_vsync < target_lead_pixels ) begin + // + becomes - in 2's compliment represenation with 1's extension... + current_genlock_delay[23:0] <= current_genlock_delay[23:0] + {1'b1,timing_difference[23:1]}; + end else begin + current_genlock_delay <= current_genlock_delay; + end + end // if ( hdmi_vsync_happened_rising ) + + // In practice, because we are dividing by two to generate the offset, the + // lock can dither by a pixel around a zero point. + // So, compute "locked" based upon a bit of a slop to avoid dithering. + // The few-pixel offset is fully absorbed by the FIFO that bridges the + // LCD to the HDMI domain. + if( timing_difference[23] ) begin // negative + // technically, I've done a 1's compliment below, but who's counting? + // it's a tolerance band anyways that will be large relative to 1 typically, + // so save the adder. + if( timing_difference[23:0] > {8'b1,~lock_tolerance[15:0]} ) begin + locked <= 1; + end else begin + locked <= 0; + end + end else begin + if( timing_difference[23:0] < {8'b0,lock_tolerance[15:0]} ) begin + locked <= 1; + end else begin + locked <= 0; + end + end // else: !if( timing_difference[23] ) + + end // else: !if( rstin || reset_lock_machine ) + end // always @ (posedge hdmi_pclk) + assign hdmi_vsync_happened_rising = hdmi_vsync_happened & !hdmi_vsync_happened_d; + assign timing_difference[23:0] = measured_vsync_to_vsync[23:0] - target_lead_pixels[23:0]; + + reg hdmi_de_falling_d1; + reg hdmi_de_falling_d2; + //////////////////////////////// + ///////// line update machine + //////////////////////////////// + assign advance_write = lcd_de_falling && !lines_full; + + always @(posedge hdmi_pclk) begin + hdmi_de_falling_d1 <= hdmi_de_falling; + hdmi_de_falling_d2 <= hdmi_de_falling; + end + // advance a little bit after DE is done, to give the fifo time to asynch reset + assign advance_read = hdmi_de_falling_d2 & !lines_empty; // always assume data is ready for us...this may not be true + + assign hdmi_en = hdmi_de; + + assign lcd_en = lcd_de; + + ////////// + // Below is the line FIFO mechanism + // + // interfaces: + // lines_full / lines_empty + // hdmi_vsync_v -- resets the write and read trackers + // advance_read -- advance the FIFO one line on the read side + // advance_write -- advance the FIFO one line on the write side + // hdmi_en -- read from the fifo + // lcd_en -- write to the fifo + // + // write_init_level, read_init_level -- initial pointer settings for fifos + // line_full_level, line_empty_level -- set high/low water mark for fifos + + ///////////////////////////////// + //// line-level empty/full detectors + ///////////////////////////////// + reg lines_empty; + reg lines_full; + + // full case would be: + // --> roll direction + // write_tracker: 00100000 + // read_tracker: 00010000 + // in other words, if you were to advance the write tracker one more + // position, you'd start writing the actively read line. + always @(posedge lcd_dclk) begin + case (line_full_level) + 3'b000: begin // +2 + if( write_tracker == {read_tracker[(NUMLINES-4) : 0], + read_tracker[(NUMLINES-1) : (NUMLINES-3)]} ) begin + lines_full <= 1; + end else begin + lines_full <= 0; + end + end + 3'b001: begin // +1 + if( write_tracker == {read_tracker[(NUMLINES-3) : 0], + read_tracker[(NUMLINES-1) : (NUMLINES-2)]} ) begin + lines_full <= 1; + end else begin + lines_full <= 0; + end + end + 3'b010: begin // nominal + if( write_tracker == {read_tracker[(NUMLINES-2) : 0], read_tracker[NUMLINES-1]} ) begin + lines_full <= 1; + end else begin + lines_full <= 0; + end + end + 3'b011: begin // -1 + if( write_tracker == read_tracker ) begin + lines_full <= 1; + end else begin + lines_full <= 0; + end + end + 3'b100: begin // -2 + if( write_tracker == {read_tracker[0], read_tracker[NUMLINES-1 : 1]} ) begin + lines_full <= 1; + end else begin + lines_full <= 0; + end + end + default: begin + lines_full <= 0; // disable the tracker + end + endcase // case (line_full_level) + end + + // empty case would be: + // --> roll direction + // write_tracker: 00001000 + // read_tracker: 00010000 + // in other words, if you were to advance the read tracker one more + // position, you'd start reading the actively written line. + always @(posedge hdmi_pclk) begin + case (line_empty_level) + 3'b000: begin // +2 + if( write_tracker == {read_tracker[2:0], read_tracker[(NUMLINES-1) : 3]} ) begin + lines_empty <= 1; + end else begin + lines_empty <= 0; + end + end + 3'b001: begin // +1 + if( write_tracker == {read_tracker[1:0], read_tracker[(NUMLINES-1) : 2]} ) begin + lines_empty <= 1; + end else begin + lines_empty <= 0; + end + end + 3'b010: begin // nominal + if( write_tracker == {read_tracker[0], read_tracker[(NUMLINES-1) : 1]} ) begin + lines_empty <= 1; + end else begin + lines_empty <= 0; + end + end + 3'b011: begin // -1 + if( write_tracker == read_tracker ) begin + lines_empty <= 1; + end else begin + lines_empty <= 0; + end + end + 3'b100: begin // -2 + if( write_tracker == {read_tracker[(NUMLINES-2) : 0], read_tracker[NUMLINES-1]} ) begin + lines_empty <= 1; + end else begin + lines_empty <= 0; + end + end + default: begin + lines_empty <= 0; + end + endcase // case (line_empty_level) + end + + ///////////////////////////////// + ///// active line tracking bits + ///////////////////////////////// + parameter NUMLINES = 4'h8; + + reg [(NUMLINES-1) : 0] read_tracker; + reg [(NUMLINES-1) : 0] write_tracker; + + // rolls to the "right" + // init at "empty" state + // reset line tracking at every hdmi vsync period, regardless of clock domai + always @(posedge lcd_dclk or posedge rstin) begin + if (rstin | hdmi_vsync_v) begin +// write_tracker[0] <= 1; +// write_tracker[1] <= 0; + write_tracker[3:0] <= write_init_level; + write_tracker[(NUMLINES-1):4] <= 0; + end else if (advance_write) begin + write_tracker <= {write_tracker[0], write_tracker[(NUMLINES-1) : 1]}; + end + end + + always @(posedge hdmi_pclk or posedge rstin) begin + if (rstin | hdmi_vsync_v) begin +// read_tracker[0] <= 0; +// read_tracker[1] <= 1; + read_tracker[3:0] <= read_init_level; + read_tracker[(NUMLINES-1):4] <= 0; + end else if (advance_read) begin + read_tracker <= {read_tracker[0], read_tracker[(NUMLINES-1) : 1]}; + end + end + + ////// + //// fifo output mux + ////// + reg [17:0] fifo_muxout; + always @(read_tracker, fifo_dout0, fifo_dout1, fifo_dout2, fifo_dout3, + fifo_dout4, fifo_dout5, fifo_dout6, fifo_dout7) begin + case( read_tracker ) // synthesis parallel_case full_case + 8'b00000001: fifo_muxout = fifo_dout0; + 8'b00000010: fifo_muxout = fifo_dout1; + 8'b00000100: fifo_muxout = fifo_dout2; + 8'b00001000: fifo_muxout = fifo_dout3; + 8'b00010000: fifo_muxout = fifo_dout4; + 8'b00100000: fifo_muxout = fifo_dout5; + 8'b01000000: fifo_muxout = fifo_dout6; + 8'b10000000: fifo_muxout = fifo_dout7; + endcase // case ( read_tracker ) + end // always @ (read_tracker, fifo_dout0, fifo_dout1, fifo_dout2, fifo_dout3,... + assign fifo_read[(NUMLINES-1) : 0] = read_tracker[(NUMLINES-1) : 0]; + + assign fifo_write[(NUMLINES-1) : 0] = write_tracker[(NUMLINES-1) : 0]; + + //// final connections to hdmi path + // add pipeline register here + always @(posedge hdmi_pclk or posedge rstin) begin + if( rstin ) begin + hdmi_g <= 8'b0; + hdmi_b <= 8'b0; + hdmi_r <= 8'b0; + end else begin + {hdmi_b[7:2],hdmi_g[7:2],hdmi_r[7:2]} <= fifo_muxout[17:0]; + // duplicate LSB's so as to not offset colors by the LSB amount + hdmi_g[1:0] <= {hdmi_g[2],hdmi_g[2]}; + hdmi_r[1:0] <= {hdmi_r[2],hdmi_r[2]}; + hdmi_b[1:0] <= {hdmi_b[2],hdmi_b[2]}; + end // else: !if( rstin ) + end // always @ (posedge hdmi_pclk or posedge rstin) + + + ///////////////////////////////// + ////// line fifos + ////// need to manually instantiate every instance, I don't think you can do array + ////// instantiations... + ///////////////////////////////// + + ////// warning: hard-coded against NUMLINES.... + // basically, reset myself if and only if I am the active line, and the read just finished. + + assign fifo_din = {lcd_b[5:0], lcd_g[5:0], lcd_r[5:0]}; + + assign fifo_rst[0] = fifo_read[0] & hdmi_de_falling; // this is not parameterized + assign fifo_rst[1] = fifo_read[1] & hdmi_de_falling; + assign fifo_rst[2] = fifo_read[2] & hdmi_de_falling; + assign fifo_rst[3] = fifo_read[3] & hdmi_de_falling; + assign fifo_rst[4] = fifo_read[4] & hdmi_de_falling; + assign fifo_rst[5] = fifo_read[5] & hdmi_de_falling; + assign fifo_rst[6] = fifo_read[6] & hdmi_de_falling; + assign fifo_rst[7] = fifo_read[7] & hdmi_de_falling; + + fifo_2kx18 line_fifo0( + .rst(rstin | fifo_rst[0] | hdmi_vsync_v), + + .wr_clk(lcd_dclk), + .din(fifo_din[17:0]), +// .wr_en(fifo_write[0] & !fifo_full[0] & lcd_en), + .wr_en(fifo_write[0] & lcd_en), +// .full(fifo_full[0]), +// .overflow(fifo_over[0]), + + .rd_clk(hdmi_pclk), +// .rd_en(fifo_read[0] & !fifo_empty[0] & hdmi_en), + .rd_en(fifo_read[0] & hdmi_en), + .dout(fifo_dout0[17:0]) +// .empty(fifo_empty[0]), +// .underflow(fifo_under[0]) + ); + + fifo_2kx18 line_fifo1( + .rst(rstin | fifo_rst[1] | hdmi_vsync_v), + + .wr_clk(lcd_dclk), + .din(fifo_din[17:0]), +// .wr_en(fifo_write[1] & !fifo_full[1] & lcd_en), + .wr_en(fifo_write[1] & lcd_en), +// .full(fifo_full[1]), +// .overflow(fifo_over[1]), + + .rd_clk(hdmi_pclk), +// .rd_en(fifo_read[1] & !fifo_empty[1] & hdmi_en), + .rd_en(fifo_read[1] & hdmi_en), + .dout(fifo_dout1[17:0]) +// .empty(fifo_empty[1]), +// .underflow(fifo_under[1]) + ); + + fifo_2kx18 line_fifo2( + .rst(rstin | fifo_rst[2] | hdmi_vsync_v), + + .wr_clk(lcd_dclk), + .din(fifo_din[17:0]), +// .wr_en(fifo_write[2] & !fifo_full[2] & lcd_en), + .wr_en(fifo_write[2] & lcd_en), +// .full(fifo_full[2]), +// .overflow(fifo_over[2]), + + .rd_clk(hdmi_pclk), +// .rd_en(fifo_read[2] & !fifo_empty[2] & hdmi_en), + .rd_en(fifo_read[2] & hdmi_en), + .dout(fifo_dout2[17:0]) +// .empty(fifo_empty[2]), +// .underflow(fifo_under[2]) + ); + + fifo_2kx18 line_fifo3( + .rst(rstin | fifo_rst[3] | hdmi_vsync_v), + + .wr_clk(lcd_dclk), + .din(fifo_din[17:0]), +// .wr_en(fifo_write[3] & !fifo_full[3] & lcd_en), + .wr_en(fifo_write[3] & lcd_en), +// .full(fifo_full[3]), +// .overflow(fifo_over[3]), + + .rd_clk(hdmi_pclk), +// .rd_en(fifo_read[3] & !fifo_empty[3] & hdmi_en), + .rd_en(fifo_read[3] & hdmi_en), + .dout(fifo_dout3[17:0]) +// .empty(fifo_empty[3]), +// .underflow(fifo_under[3]) + ); + + fifo_2kx18 line_fifo4( + .rst(rstin | fifo_rst[4] | hdmi_vsync_v), + + .wr_clk(lcd_dclk), + .din(fifo_din[17:0]), +// .wr_en(fifo_write[4] & !fifo_full[4] & lcd_en), + .wr_en(fifo_write[4] & lcd_en), +// .full(fifo_full[4]), +// .overflow(fifo_over[4]), + + .rd_clk(hdmi_pclk), +// .rd_en(fifo_read[4] & !fifo_empty[4] & hdmi_en), + .rd_en(fifo_read[4] & hdmi_en), + .dout(fifo_dout4[17:0]) +// .empty(fifo_empty[4]), +// .underflow(fifo_under[4]) + ); + + fifo_2kx18 line_fifo5( + .rst(rstin | fifo_rst[5] | hdmi_vsync_v), + + .wr_clk(lcd_dclk), + .din(fifo_din[17:0]), +// .wr_en(fifo_write[5] & !fifo_full[5] & lcd_en), + .wr_en(fifo_write[5] & lcd_en), +// .full(fifo_full[5]), +// .overflow(fifo_over[5]), + + .rd_clk(hdmi_pclk), +// .rd_en(fifo_read[5] & !fifo_empty[5] & hdmi_en), + .rd_en(fifo_read[5] & hdmi_en), + .dout(fifo_dout5[17:0]) +// .empty(fifo_empty[5]), +// .underflow(fifo_under[5]) + ); + + fifo_2kx18 line_fifo6( + .rst(rstin | fifo_rst[6] | hdmi_vsync_v), + + .wr_clk(lcd_dclk), + .din(fifo_din[17:0]), +// .wr_en(fifo_write[6] & !fifo_full[6] & lcd_en), + .wr_en(fifo_write[6] & lcd_en), +// .full(fifo_full[6]), +// .overflow(fifo_over[6]), + + .rd_clk(hdmi_pclk), +// .rd_en(fifo_read[6] & !fifo_empty[6] & hdmi_en), + .rd_en(fifo_read[6] & hdmi_en), + .dout(fifo_dout6[17:0]) +// .empty(fifo_empty[6]), +// .underflow(fifo_under[6]) + ); + + fifo_2kx18 line_fifo7( + .rst(rstin | fifo_rst[7] | hdmi_vsync_v), + + .wr_clk(lcd_dclk), + .din(fifo_din[17:0]), +// .wr_en(fifo_write[7] & !fifo_full[7] & lcd_en), + .wr_en(fifo_write[7] & lcd_en), +// .full(fifo_full[7]), +// .overflow(fifo_over[7]), + + .rd_clk(hdmi_pclk), +// .rd_en(fifo_read[7] & !fifo_empty[7] & hdmi_en), + .rd_en(fifo_read[7] & hdmi_en), + .dout(fifo_dout7[17:0]) +// .empty(fifo_empty[7]), +// .underflow(fifo_under[7]) + ); + + assign dummy = 1'b0; +// assign dummy = !( (fifo_under[(NUMLINES-1) : 0] != 0) || lines_empty); + // trigger LED flash if we see any fifo go underflow, or if the overall line structure is empty + + ///////////////////////////////////// + ///// jellybean routines (synchronizers, rising edge finders, etc. etc.) + ///////////////////////////////////// + // clean up the sync signals, as they are invalid + // outside of control periods and have a programmable polarity + always @(posedge hdmi_pclk or posedge rstin) begin + if( rstin ) begin + hdmi_hsync_v <= 0; + hdmi_vsync_v <= 0; + + hdmi_hsync_v2 <= 0; + hdmi_vsync_v2 <= 0; + + hdmi_de_d <= 0; + + hdmi_first_de <= 0; + hdmi_first_de_state <= 0; + + lcd_vsync_1hdmi <= 0; + lcd_vsync_2hdmi <= 0; + lcd_vsync_hdmi <= 0; + end else begin + hdmi_de_d <= hdmi_de; + if( hdmi_cv ) begin + hdmi_hsync_v <= hdmi_hsync ^ !hdmi_sync_pol; + hdmi_vsync_v <= hdmi_vsync ^ !hdmi_sync_pol; + end else begin + hdmi_hsync_v <= hdmi_hsync_v; + hdmi_vsync_v <= hdmi_vsync_v; + end + + hdmi_hsync_v2 <= hdmi_hsync_v; // just a delayed version + hdmi_vsync_v2 <= hdmi_vsync_v; // just a delayed version + + if( hdmi_vsync_v ) begin + hdmi_first_de <= 0; + hdmi_first_de_state <= 0; + end else begin + if( hdmi_de_rising && (hdmi_first_de_state == 0) ) begin + hdmi_first_de <= 1; + hdmi_first_de_state <= 1; + end else begin + hdmi_first_de <= 0; + hdmi_first_de_state <= hdmi_first_de_state; + end + end // else: !if( hdmi_vsync_v ) + + lcd_vsync_2hdmi <= lcd_vsync; + lcd_vsync_1hdmi <= lcd_vsync_2hdmi; + lcd_vsync_hdmi <= lcd_vsync_1hdmi; + end // else: !if( rstin ) + end // always @ (posedge hdmi_pclk or posedge rstin) + assign hdmi_hsync_rising = hdmi_hsync_v && !hdmi_hsync_v2; + assign hdmi_vsync_rising = hdmi_vsync_v && !hdmi_vsync_v2; + assign hdmi_de_rising = hdmi_de && !hdmi_de_d; + assign hdmi_de_falling = !hdmi_de && hdmi_de_d; + assign lcd_vsync_rising__hdmi = !lcd_vsync_hdmi & lcd_vsync_1hdmi; + + reg lcd_vsync_rising; + reg lcd_vsync_falling; + reg lcd_vsync_d; + + assign lcd_de_vsync = lcd_de & !(lcd_vsync ^ !lcd_sync_pol); + // utility to find rising edges + always @(posedge lcd_dclk or posedge rstin) begin + if(rstin) begin + hdmi_hsync_rising__lcd <= 0; + hdmi_vsync_rising__lcd <= 0; + hdmi_vsync_falling__lcd <= 0; + hdmi_hsync_d__lcd <= 0; + hdmi_vsync_d__lcd <= 0; + + lcd_de_d <= 0; + lcd_de_rising <= 0; + + lcd_vsync_rising <= 0; + lcd_vsync_d <= 0; + lcd_de_rising <= 0; + + lcd_hsync_d <= 0; + end else begin // if (rstin) + lcd_hsync_d <= lcd_hsync ^ !lcd_sync_pol; + lcd_hsync_rising <= (lcd_hsync ^ !lcd_sync_pol) && !lcd_hsync_d; + + lcd_vsync_d <= lcd_vsync ^ !lcd_sync_pol; + lcd_vsync_rising <= (lcd_vsync ^ !lcd_sync_pol) && !lcd_vsync_d; + lcd_vsync_falling <= !(lcd_vsync ^ !lcd_sync_pol) && lcd_vsync_d; + + hdmi_hsync_d__lcd <= hdmi_hsync__lcd; + hdmi_vsync_d__lcd <= hdmi_vsync__lcd; + lcd_de_d <= lcd_de_vsync; + + if( hdmi_hsync__lcd && !hdmi_hsync_d__lcd ) + hdmi_hsync_rising__lcd <= 1; + else + hdmi_hsync_rising__lcd <= 0; + + if( hdmi_vsync__lcd && !hdmi_vsync_d__lcd ) + hdmi_vsync_rising__lcd <= 1; + else + hdmi_vsync_rising__lcd <= 0; + + if( !hdmi_vsync__lcd && hdmi_vsync_d__lcd ) + hdmi_vsync_falling__lcd <= 1; + else + hdmi_vsync_falling__lcd <= 0; + + if( lcd_de_vsync && !lcd_de_d ) + lcd_de_rising <= 1; + else + lcd_de_rising <= 0; + + if( !lcd_de_vsync && lcd_de_d ) + lcd_de_falling <= 1; + else + lcd_de_falling <= 0; + end // else: !if(rstin) + end // always @ (posedge lcd_dclk or posedge rstin) + + always @(posedge hdmi_pclk or posedge rstin) begin + if(rstin) begin + lcd_de_d__hdmi <= 0; + end else begin + lcd_de_d__hdmi <= lcd_de__hdmi; + end + end + assign lcd_de_rising__hdmi = lcd_de__hdmi && !lcd_de_d__hdmi; + assign lcd_de_falling__hdmi = !lcd_de__hdmi && lcd_de_d__hdmi; + + // cross-domain synchronization + always @(posedge lcd_dclk or posedge rstin) begin + if(rstin) begin + hdmi_vsync_s__lcd <= 0; + hdmi_vsync__lcd <= 0; + hdmi_hsync_s__lcd <= 0; + hdmi_hsync__lcd <= 0; + end else begin + hdmi_vsync_s__lcd <= hdmi_vsync_v; + hdmi_vsync__lcd <= hdmi_vsync_s__lcd; + hdmi_hsync_s__lcd <= hdmi_hsync_v; + hdmi_hsync__lcd <= hdmi_hsync_s__lcd; + end // else: !if(rstin) + end // always @ (posedge lcd_dclk or posedge rstin) + + always @(posedge hdmi_pclk or posedge rstin) begin + if(rstin) begin + lcd_de_s__hdmi <= 0; + lcd_de__hdmi <= 0; + + lcd_overflow_s__hdmi <= 0; + lcd_overflow__hdmi <= 0; + end else begin + lcd_de_s__hdmi <= lcd_de_vsync; + lcd_de__hdmi <= lcd_de_s__hdmi; + + lcd_overflow_s__hdmi <= lcd_overflow; + lcd_overflow__hdmi <= lcd_overflow_s__hdmi; + end + end // always @ (posedge hdmi_pclk or posedge rstin) + +endmodule // lcd_input -- cgit v1.2.3