
module Top(
    input      CLK_IN,
    inout      usb_dxp_io     ,
    inout      usb_dxn_io     ,
    input      usb_rxdp_i     ,
    input      usb_rxdn_i     ,
    output     usb_pullup_en_o,
    inout      usb_term_dp_io ,
    inout      usb_term_dn_io ,
    
    inout hard_ck_n,
    inout hard_ck_p,
    inout hard_d0_n,
    inout hard_d0_p,
    inout hard_d1_n,
    inout hard_d1_p,

    inout soft_ck_n,
    inout soft_ck_p,
    inout soft_d0_n,
    inout soft_d0_p,
    inout soft_d1_n,
    inout soft_d1_p,

    inout       cam0_scl,
    inout       cam0_sda,
    output      cam1_scl,
    output      cam1_sda,
    inout       cam2_scl,
    inout       cam2_sda

,   output      uart_tx

);


localparam ENDPT_VIDEO_CONFIG = 4'd0;
localparam ENDPT_VIDEO_STREAMING = 4'd1;

wire [1:0]  PHY_XCVRSELECT      ;
wire        PHY_TERMSELECT      ;
wire [1:0]  PHY_OPMODE          ;
wire [1:0]  PHY_LINESTATE       ;
wire        PHY_TXVALID         ;
wire        PHY_TXREADY         ;
wire        PHY_RXVALID         ;
wire        PHY_RXACTIVE        ;
wire        PHY_RXERROR         ;
wire [7:0]  PHY_DATAIN          ;
wire [7:0]  PHY_DATAOUT         ;
wire        PHY_CLKOUT          ;
wire [15:0] DESCROM_RADDR       ;
wire [ 7:0] DESC_INDEX          ;
wire [ 7:0] DESC_TYPE           ;
wire [ 7:0] DESCROM_RDAT        ;
wire [15:0] DESC_DEV_ADDR       ;
wire [15:0] DESC_DEV_LEN        ;
wire [15:0] DESC_QUAL_ADDR      ;
wire [15:0] DESC_QUAL_LEN       ;
wire [15:0] DESC_FSCFG_ADDR     ;
wire [15:0] DESC_FSCFG_LEN      ;
wire [15:0] DESC_HSCFG_ADDR     ;
wire [15:0] DESC_HSCFG_LEN      ;
wire [15:0] DESC_OSCFG_ADDR     ;
wire [15:0] DESC_HIDRPT_ADDR    ;
wire [15:0] DESC_HIDRPT_LEN     ;
wire [15:0] DESC_BOS_ADDR       ;
wire [15:0] DESC_BOS_LEN        ;
wire [15:0] DESC_STRLANG_ADDR   ;
wire [15:0] DESC_STRVENDOR_ADDR ;
wire [15:0] DESC_STRVENDOR_LEN  ;
wire [15:0] DESC_STRPRODUCT_ADDR;
wire [15:0] DESC_STRPRODUCT_LEN ;
wire [15:0] DESC_STRSERIAL_ADDR ;
wire [15:0] DESC_STRSERIAL_LEN  ;
wire        DESCROM_HAVE_STRINGS;
wire        RESET;
reg  [7:0]  rst_cnt;
wire [7:0]  usb_txdat;
wire [11:0] usb_txdat_len;
wire        usb_txcork;
wire        usb_txpop;
wire        usb_txact;
wire [7:0]  usb_rxdat;
wire        usb_rxval;
wire        usb_rxpktval;
wire        usb_rxact;
wire        usb_rxrdy;
wire [3:0]  endpt_sel;
wire        rx_fifo_wren;
wire        rx_fifo_empty;
reg         rx_fifo_rden;
wire [7:0]  rx_fifo_data;
wire        rx_fifo_dval;
reg         rx_fifo_dval_d0;
wire        rx_fifo_dval_rise;
wire [9:0]  tx_fifo_wnum;
wire        tx_fifo_empty;
wire [7:0]  tx_fifo_rdat;
wire        tx_fifo_rd;
wire        setup_active;
wire        setup_val;
wire [7:0]  setup_data;
wire        endpt0_send;
wire [7:0]  endpt0_dat;
wire        pll_locked;
wire        uart_en;
wire [31:0] uart_dte_rate;
wire [7:0]  uart_char_format;
wire [7:0]  uart_parity_type;
wire [7:0]  uart_data_bits;
wire [11:0] video_config_txdat_len;
wire [15:0] uart_tx_data    ;
wire        uart_tx_data_val;
wire        uart_tx_busy    ;
wire [15:0] uart_rx_data    ;
wire        uart_rx_data_val;
wire        uart_rts;
wire        uart_cts;
wire        uart_txd;
wire        uart_rxd;
wire        ep_usb_rxrdy;
wire        ep_usb_txcork;
wire [11:0] ep_usb_txlen;
wire [7:0]  ep_usb_txdat;
wire        ep2_rx_dval;
wire [7:0]  ep2_rx_data;
wire [7:0]  inf_alter_i;
wire [7:0]  inf_alter_o;
wire [7:0]  inf_sel_o;
wire        inf_set_o;
reg  [7:0]  interface0_alter;
reg  [7:0]  interface1_alter;

wire [7:0] buffer_data;
wire [15:0] buffer_length;
wire buffer_last;
wire buffer_ready;
wire [15:0] buffer_addr;
wire buffer_complete;

wire        cam0_pixel_clock;
wire        cam0_vs;
wire        cam0_de;
wire [7:0]  cam0_data;

wire        cam2_pixel_clock;
wire        cam2_vs;
wire        cam2_de;
wire [7:0]  cam2_data;

wire        cam0_cropped_de;
wire        cam2_cropped_de;

wire        combined_pixel_clock;
wire        combined_vs;
wire        combined_de;
wire [7:0]  combined_data;

wire [7:0]  jpeg_data;
wire        jpeg_de;
wire        jpeg_done;
reg         jpeg_vs;

// Only doing this because we have a lot of horizontal blanking
// we are ignoring 1920-400 = 1520 pixels in every line, so we should be able 
// to push out the 800 pixel wide line without problem. Should be.
assign combined_pixel_clock = cam0_pixel_clock;



// show the pixel clock divided by 128 on an output pin
reg [6:0] pixel_clock_count;
always @(posedge cam0_pixel_clock) begin
    pixel_clock_count <= pixel_clock_count + 'd1;
end

assign uart_tx = pixel_clock_count[6];

//==============================================================
//======PLL 


wire clk_24m;
wire fclk_480M;

// First stage PLL - 50MHz to 24MHz

Gowin_PLL_24 u_pll_24(
    .clkout0(clk_24m), //output clkout0
    .clkin(CLK_IN) //input clkin
);

// Second stage PLL - 24MHz to 960MHz and 60MHz
Gowin_PLL u_pll(
    .lock(pll_locked), //output lock
    .clkout0(fclk_480M), //output clkout0
    .clkout1(PHY_CLKOUT), //output clkout1
    .clkin(clk_24m) //input clkin
);


assign RESET = ~pll_locked;
//==============================================================
//======
//assign cam2_sda = jpeg_vs;
//assign cam2_scl = cam0_cropped_de;

assign cam1_sda = cam0_vs;
assign cam1_scl = cam0_de;

//==============================================================
//======Interface Setting
assign inf_alter_i = (inf_sel_o == 0) ? interface0_alter :
                     (inf_sel_o == 1) ? interface1_alter : 8'd0;
always@(posedge PHY_CLKOUT, posedge RESET   ) begin
    if (RESET) begin
        interface0_alter <= 'd0;
        interface1_alter <= 'd0;
    end
    else begin
        if (inf_set_o) begin
            if (inf_sel_o == 0) begin
                interface0_alter <= inf_alter_o;
            end
            else if (inf_sel_o == 1) begin
                interface1_alter <= inf_alter_o;
            end
        end
    end
end
//==============================================================
//======Device Controller
USB_Device_Controller_Top u_usb_device_controller_top (
     .clk_i                 (PHY_CLKOUT          )
    ,.reset_i               (RESET               )
    ,.usbrst_o              (usb_busreset        )
    ,.highspeed_o           (usb_highspeed       )
    ,.suspend_o             (usb_suspend         )
    ,.online_o              (usb_online          )
    ,.txdat_i               (usb_txdat           )
    ,.txval_i               (endpt0_send&(endpt_sel==ENDPT_VIDEO_CONFIG))
    ,.txdat_len_i           (usb_txdat_len       )
    ,.txcork_i              (usb_txcork          )
    ,.txiso_pid_i           (4'b0011             )
    ,.txpop_o               (usb_txpop           )
    ,.txact_o               (usb_txact           )
    ,.txpktfin_o            (usb_txpktfin        )
    ,.rxdat_o               (usb_rxdat           )
    ,.rxval_o               (usb_rxval           )
    ,.rxact_o               (usb_rxact           )
    ,.rxrdy_i               (usb_rxrdy           )
    ,.rxpktval_o            (usb_rxpktval        )
    ,.setup_o               (setup_active        )
    ,.endpt_o               (endpt_sel           )
    ,.sof_o                 (usb_sof             )
    ,.inf_alter_i           (inf_alter_i         )
    ,.inf_alter_o           (inf_alter_o         )
    ,.inf_sel_o             (inf_sel_o           )
    ,.inf_set_o             (inf_set_o           )
    ,.descrom_rdata_i       (DESCROM_RDAT        )
    ,.descrom_raddr_o       (DESCROM_RADDR       )
    ,.desc_index_o          (DESC_INDEX          )
    ,.desc_type_o           (DESC_TYPE           )
    ,.desc_dev_addr_i       (DESC_DEV_ADDR       )
    ,.desc_dev_len_i        (DESC_DEV_LEN        )
    ,.desc_qual_addr_i      (DESC_QUAL_ADDR      )
    ,.desc_qual_len_i       (DESC_QUAL_LEN       )
    ,.desc_fscfg_addr_i     (DESC_FSCFG_ADDR     )
    ,.desc_fscfg_len_i      (DESC_FSCFG_LEN      )
    ,.desc_hscfg_addr_i     (DESC_HSCFG_ADDR     )
    ,.desc_hscfg_len_i      (DESC_HSCFG_LEN      )
    ,.desc_oscfg_addr_i     (DESC_OSCFG_ADDR     )
    ,.desc_hidrpt_addr_i    (16'd0               )//DESC_HIDRPT_ADDR
    ,.desc_hidrpt_len_i     (16'd0               )//DESC_HIDRPT_LEN
    ,.desc_bos_addr_i       (16'd0               )//DESC_BOS_ADDR
    ,.desc_bos_len_i        (16'd0               )//DESC_BOS_LEN
    ,.desc_strlang_addr_i   (DESC_STRLANG_ADDR   )
    ,.desc_strvendor_addr_i (DESC_STRVENDOR_ADDR )
    ,.desc_strvendor_len_i  (DESC_STRVENDOR_LEN  )
    ,.desc_strproduct_addr_i(DESC_STRPRODUCT_ADDR)
    ,.desc_strproduct_len_i (DESC_STRPRODUCT_LEN )
    ,.desc_strserial_addr_i (DESC_STRSERIAL_ADDR )
    ,.desc_strserial_len_i  (DESC_STRSERIAL_LEN  )
    ,.desc_have_strings_i   (DESCROM_HAVE_STRINGS)
    
    ,.utmi_dataout_o        (PHY_DATAOUT       )
    ,.utmi_txvalid_o        (PHY_TXVALID       )
    ,.utmi_txready_i        (PHY_TXREADY       )
    ,.utmi_datain_i         (PHY_DATAIN        )
    ,.utmi_rxactive_i       (PHY_RXACTIVE      )
    ,.utmi_rxvalid_i        (PHY_RXVALID       )
    ,.utmi_rxerror_i        (PHY_RXERROR       )
    ,.utmi_linestate_i      (PHY_LINESTATE     )
    ,.utmi_opmode_o         (PHY_OPMODE        )
    ,.utmi_xcvrselect_o     (PHY_XCVRSELECT    )
    ,.utmi_termselect_o     (PHY_TERMSELECT    )
    ,.utmi_reset_o          (PHY_RESET         )
);

//==============================================================
//======USB Device descriptor Demo
usb_desc_uvc
#(

     .VENDORID    (16'h35BD)
    ,.PRODUCTID   (16'h0202)
    ,.VERSIONBCD  (16'h0100)
    ,.HSSUPPORT   (1       )
    ,.SELFPOWERED (1       )
)
u_usb_desc (
     .CLK                    (PHY_CLKOUT          )
    ,.RESET                  (RESET               )
    ,.i_pid                  (16'd0               )
    ,.i_vid                  (16'd0               )
    ,.i_descrom_raddr        (DESCROM_RADDR       )
    ,.o_descrom_rdat         (DESCROM_RDAT        )
    ,.o_desc_dev_addr        (DESC_DEV_ADDR       )
    ,.o_desc_dev_len         (DESC_DEV_LEN        )
    ,.o_desc_qual_addr       (DESC_QUAL_ADDR      )
    ,.o_desc_qual_len        (DESC_QUAL_LEN       )
    ,.o_desc_fscfg_addr      (DESC_FSCFG_ADDR     )
    ,.o_desc_fscfg_len       (DESC_FSCFG_LEN      )
    ,.o_desc_hscfg_addr      (DESC_HSCFG_ADDR     )
    ,.o_desc_hscfg_len       (DESC_HSCFG_LEN      )
    ,.o_desc_oscfg_addr      (DESC_OSCFG_ADDR     )
    ,.o_desc_strlang_addr    (DESC_STRLANG_ADDR   )
    ,.o_desc_strvendor_addr  (DESC_STRVENDOR_ADDR )
    ,.o_desc_strvendor_len   (DESC_STRVENDOR_LEN  )
    ,.o_desc_strproduct_addr (DESC_STRPRODUCT_ADDR)
    ,.o_desc_strproduct_len  (DESC_STRPRODUCT_LEN )
    ,.o_desc_strserial_addr  (DESC_STRSERIAL_ADDR )
    ,.o_desc_strserial_len   (DESC_STRSERIAL_LEN  )
    ,.o_descrom_have_strings (DESCROM_HAVE_STRINGS)
);

//==============================================================
//======USB SoftPHY
USB2_0_SoftPHY_Top u_USB_SoftPHY_Top
(
     .clk_i            (PHY_CLKOUT     )
    ,.rst_i            (PHY_RESET      )
    ,.fclk_i           (fclk_480M      )
    ,.pll_locked_i     (pll_locked     )
    ,.utmi_data_out_i  (PHY_DATAOUT    )
    ,.utmi_txvalid_i   (PHY_TXVALID    )
    ,.utmi_op_mode_i   (PHY_OPMODE     )
    ,.utmi_xcvrselect_i(PHY_XCVRSELECT )
    ,.utmi_termselect_i(PHY_TERMSELECT )
    ,.utmi_data_in_o   (PHY_DATAIN     )
    ,.utmi_txready_o   (PHY_TXREADY    )
    ,.utmi_rxvalid_o   (PHY_RXVALID    )
    ,.utmi_rxactive_o  (PHY_RXACTIVE   )
    ,.utmi_rxerror_o   (PHY_RXERROR    )
    ,.utmi_linestate_o (PHY_LINESTATE  )
    ,.usb_dxp_io       (usb_dxp_io     )
    ,.usb_dxn_io       (usb_dxn_io     )
    ,.usb_rxdp_i       (usb_rxdp_i     )
    ,.usb_rxdn_i       (usb_rxdn_i     )
    ,.usb_pullup_en_o  (usb_pullup_en_o)
    ,.usb_term_dp_io   (usb_term_dp_io )
    ,.usb_term_dn_io   (usb_term_dn_io )
);

// ===== USB UVC Config
// ===== Responds to control requests on endpoint 0
usb_video_config u_video_config
(
    .PHY_CLKOUT(PHY_CLKOUT),
    .RESET_IN(usb_busreset),
    .setup_active_i(setup_active),
    .usb_rxval_i(usb_rxval && (endpt_sel == ENDPT_VIDEO_CONFIG)),
    .usb_rxact_i(usb_rxact && (endpt_sel == ENDPT_VIDEO_CONFIG)),
    .usb_rxdat_i((endpt_sel == ENDPT_VIDEO_CONFIG) ? usb_rxdat : 8'd0 ),
    .usb_rxrdy_o(ep_usb_rxrdy),
    .usb_txact_i(usb_txact && (endpt_sel == ENDPT_VIDEO_CONFIG)),
    .usb_txpop_i(usb_txpop && (endpt_sel == ENDPT_VIDEO_CONFIG)),
    .usb_txdat_len_o(video_config_txdat_len),
    .usb_txdat_o(endpt0_dat),
    .usb_txval_o(endpt0_send)
);

assign usb_rxrdy     = ep_usb_rxrdy;
assign usb_txcork    = (endpt_sel ==ENDPT_VIDEO_CONFIG) ? 1'b0 : ep_usb_txcork;
assign usb_txdat     = (endpt_sel==ENDPT_VIDEO_CONFIG) ? endpt0_dat : ep_usb_txdat;
assign usb_txdat_len = (endpt_sel ==ENDPT_VIDEO_CONFIG) ? video_config_txdat_len : ep_usb_txlen;

// ==== Video generator
// ==== creates grayscale stripe pattern

MIPI_CSI_HardPhy_Rx u_mipi_rx(
    .RESET_N(~RESET),
    .OSC_50M(CLK_IN),
    .MIPI_HARD_CLK_N(hard_ck_n),
    .MIPI_HARD_CLK_P(hard_ck_p),
    .MIPI_HARD_D0_N(hard_d0_n),
    .MIPI_HARD_D0_P(hard_d0_p),
    .MIPI_HARD_D1_N(hard_d1_n),
    .MIPI_HARD_D1_P(hard_d1_p),
    .O_PIXEL_CLK(cam0_pixel_clock),
    .O_PIXEL_VS(cam0_vs),
    .O_PIXEL_DE(cam0_de),
    .O_PIXEL_DATA(cam0_data)
);

MIPI_CSI_SoftPhy_Rx u_mipi_rx_soft(
    .RESET_N(~RESET),
    .OSC_50M(CLK_IN),
    .MIPI_SOFT_CLK_N(soft_ck_n),
    .MIPI_SOFT_CLK_P(soft_ck_p),
    .MIPI_SOFT_D0_N(soft_d0_n),
    .MIPI_SOFT_D0_P(soft_d0_p),
    .MIPI_SOFT_D1_N(soft_d1_n),
    .MIPI_SOFT_D1_P(soft_d1_p),
    .O_PIXEL_CLK(cam2_pixel_clock),
    .O_PIXEL_VS(cam2_vs),
    .O_PIXEL_DE(cam2_de),
    .O_PIXEL_DATA(cam2_data)
);


//================================================================
//OV5647 Camera
//----------------------------

wire cam0_sda_output;
wire cam0_sda_ioen;
wire cam2_sda_output;
wire cam2_sda_ioen;

IOBUF cam0_sda_tbuf (
    .I(cam0_sda_output),
    .OEN(cam0_sda_ioen | cam0_sda_output),
    .IO(cam0_sda)
);

wire cam0_scl_temp;

IOBUF cam0_scl_tbuf (
    .I(cam0_scl_temp),
    .OEN(cam0_scl_temp),
    .IO(cam0_scl)
);

IOBUF cam2_sda_tbuf (
    .I(cam2_sda_output),
    .OEN(cam2_sda_ioen | cam2_sda_output),
    .IO(cam2_sda)
);

wire cam2_scl_temp;

IOBUF cam2_scl_tbuf (
    .I(cam2_scl_temp),
    .OEN(cam2_scl_temp),
    .IO(cam2_scl)
);

//initial
OV5647_Controller u_ov5647_ctrl_cam0
(	.clk            ( CLK_IN            )   // 50Mhz clock signal
,   .rstn           ( ~RESET            )
,   .config_finished( /* unused */      )   // Flag to indicate that the configuration is finished
,   .sioc           ( cam0_scl_temp     )   // SCCB interface - clock signal
,   .siod           ( cam0_sda_output          )   // SCCB interface - data signal
,   .siod_ioen      ( cam0_sda_ioen     )
,   .resetb         ( /* unused */      )   // RESET signal for OV5647
,   .pwdn           ( /* unused */      )   // PWDN signal for OV5647
);


OV5647_Controller u_ov5647_ctrl_cam2
(	.clk            ( CLK_IN            )   // 50Mhz clock signal
,   .rstn           ( ~RESET            )
,   .config_finished( /* unused */      )   // Flag to indicate that the configuration is finished
,   .sioc           ( cam2_scl_temp     )   // SCCB interface - clock signal
,   .siod           ( cam2_sda_output          )   // SCCB interface - data signal
,   .siod_ioen      ( cam2_sda_ioen     )
,   .resetb         ( /* unused */      )   // RESET signal for OV5647
,   .pwdn           ( /* unused */      )   // PWDN signal for OV5647
);


reg [15:0]  hcnt_cam0;
reg [15:0]  vcnt_cam0;
reg         cam0_de_r;

always @(posedge cam0_pixel_clock or posedge RESET) begin
    if(RESET) begin
        hcnt_cam0 <= 0;
        vcnt_cam0 <= 0;
        cam0_de_r <= 0;
    end else begin
        cam0_de_r <= cam0_de;
        if(cam0_vs) begin
            if(!cam0_de) begin
                hcnt_cam0 <= 0;
                if(cam0_de_r) begin 
                    // falling edge
                    vcnt_cam0 <= vcnt_cam0 + 16'd1;
                end
            end else begin
                hcnt_cam0 <= hcnt_cam0 + 16'd1;
            end
        end else begin
            vcnt_cam0 <= 0;
        end
    end
end

assign cam0_cropped_de = cam0_de && (vcnt_cam0 >= 340) && (vcnt_cam0 < (340+400)) && (hcnt_cam0 >= 760) && (hcnt_cam0 < (760+400));
//assign cam0_cropped_de = cam0_de;

reg [15:0]  hcnt_cam2;
reg [15:0]  vcnt_cam2;
reg         cam2_de_r;

always @(posedge cam2_pixel_clock or posedge RESET) begin
    if(RESET) begin
        hcnt_cam2 <= 0;
        vcnt_cam2 <= 0;
        cam2_de_r <= 0;
    end else begin
        cam2_de_r <= cam2_de;
        if(cam2_vs) begin
            if(!cam2_de) begin
                hcnt_cam2 <= 0;
                if(cam2_de_r) begin 
                    // falling edge
                    vcnt_cam2 <= vcnt_cam2 + 16'd1;
                end
            end else begin
                hcnt_cam2 <= hcnt_cam2 + 16'd1;
            end
        end else begin
            vcnt_cam2 <= 0;
        end
    end
end

assign cam2_cropped_de = cam2_de && (vcnt_cam2 >= 340) && (vcnt_cam2 < (340+400)) && (hcnt_cam2 >= 760) && (hcnt_cam2 < (760+400));
//assign cam2_cropped_de = cam2_de;

//vidgen u_vidgen(
//    .CLK(vid1_pixel_clk), 
//    .ARESET(RESET),
//    .VSYNC(vid1_vs),
//    .DE(vid1_de),
//    .PIX_DAT(vid1_data)
//);

//vidgen_vert u_vidgen_vert(
//    .CLK(vid2_pixel_clk), 
//    .ARESET(RESET),
//    .VSYNC(vid2_vs),
//    .DE(vid2_de),
//    .PIX_DAT(vid2_data)
//);

line_combiner u_line_comb(
   .ARESET(RESET),

   .CAM1_CLK(cam0_pixel_clock),
   .CAM1_DATA(cam0_data),
   .CAM1_VS(cam0_vs),
   .CAM1_DE(cam0_cropped_de),

   .CAM2_CLK(cam2_pixel_clock),
   .CAM2_DATA(cam2_data),
   .CAM2_VS(cam2_vs),
   .CAM2_DE(cam2_cropped_de),

   .COMBINED_CLK(combined_pixel_clock),
   .COMBINED_DATA(combined_data),
   .COMBINED_VS(combined_vs),
   .COMBINED_DE(combined_de)
);


// ==== JPEG encoder
/*
MJPEG_Encoder_Top u_jpeg(
    .clk(vidgen_pixel_clk), //input clk
    .rstn(~RESET), //input rstn
    .DE(vidgen_de), //input DE
    .data_in({vidgen_data, vidgen_data, vidgen_data}), //input [23:0] data_in (note this is RGB)
    .img_out(jpeg_data), //output [7:0] img_out
    .img_valid(jpeg_de), //output img_valid
    .img_done(jpeg_done) //output img_done
);
*/

// jpeg_top #(.IMG_SIZE_X(16'd800),.IMG_SIZE_Y(16'd400),.QUANT_Q('d50))
// u_jpeg(
//     .CLK(cam0_pixel_clock),
//     .ARESET(RESET),
//     .IMG_DATA_VALID(cam0_cropped_de),
//     .IMG_DATA(cam0_data),
//     .DATA_OUT(jpeg_data),
//     .DATA_OUT_VALID(jpeg_de),
//     .DATA_OUT_END(jpeg_done)
// );

jpeg_top #(.IMG_SIZE_X(16'd800),.IMG_SIZE_Y(16'd400),.QUANT_Q('d50))
u_jpeg(
    .CLK(combined_pixel_clock),
    .ARESET(RESET),
    .IMG_DATA_VALID(combined_de),
    .IMG_DATA(combined_data),
    .DATA_OUT(jpeg_data),
    .DATA_OUT_VALID(jpeg_de),
    .DATA_OUT_END(jpeg_done)
);




// Generate the vertical sync out of the jpeg encoder
// Little bit tricky as we should have a few cycles where
// VS goes high before data starts
// Easy way to do it is delay the jpeg data a little bit with
// some FFs
reg [7:0] jpeg_data_delay_1;
reg [7:0] jpeg_data_delay_2;
reg [7:0] jpeg_data_delay_3;
reg [3:1] jpeg_de_delay; 
reg [6:1] jpeg_done_delay;

always @(posedge combined_pixel_clock) begin
    jpeg_data_delay_1 <= jpeg_data;
    jpeg_data_delay_2 <= jpeg_data_delay_1;
    jpeg_data_delay_3 <= jpeg_data_delay_2;

    jpeg_de_delay <= {jpeg_de_delay[2:1], jpeg_de};
    jpeg_done_delay <= {jpeg_done_delay[5:1], jpeg_done};
end

// on the first jpeg_de high level, we'll set VS high
// data will follow 3 cycles later due to our delays (above)
// on jpeg_done, we'll wait about 6 cycles and then set VS low

always @(posedge combined_pixel_clock or posedge RESET) begin
    if(RESET) begin
        jpeg_vs <= 1'b0;
    end 
    else begin
        if(!jpeg_vs) begin
            if(jpeg_de) jpeg_vs <= 1'b1;
        end else begin
            // only drop VS after done (delayed 6 cycles)
            if(jpeg_done_delay[6]) jpeg_vs <= 1'b0;
        end
    end
end



// ==== Packet sender
// ==== Sends bulk video data over USB

video_packet_sender #(
    .VIDEO_STREAM_ENDPOINT(ENDPT_VIDEO_STREAMING)
)
u_video_sender (
    .clk(PHY_CLKOUT),
    .areset(RESET),
    
    .txact(usb_txact & (endpt_sel == ENDPT_VIDEO_STREAMING)),
    .txpop(usb_txpop & (endpt_sel == ENDPT_VIDEO_STREAMING)),
    .pktfin(usb_txpktfin & (endpt_sel == ENDPT_VIDEO_STREAMING)),
    .txdat(ep_usb_txdat),
    .txcork(ep_usb_txcork),
    .txdat_len(ep_usb_txlen),

    .video_buffer_data(buffer_data),
    .video_buffer_length(buffer_length),
    .last_buffer_of_frame(buffer_last),
    .buffer_ready(buffer_ready),
    .buffer_address(buffer_addr),
    .buffer_complete(buffer_complete)
);
assign buffer_length[15:12] = 4'b0; // extra bits. not used.

reg vid_dbl_buf_reset;
reg [3:0] vid_dbl_buf_reset_cnt;

wire vid_dbl_buf_overrun;

always @(posedge combined_pixel_clock or posedge RESET) begin
    if(RESET) begin
        vid_dbl_buf_reset <= 1'b1;
        vid_dbl_buf_reset_cnt <= 4'd0;
    end else begin

        if(vid_dbl_buf_overrun) begin
            vid_dbl_buf_reset_cnt <= 4'd0;
        end else begin
            if(vid_dbl_buf_reset_cnt != 4'd15) begin
                vid_dbl_buf_reset_cnt <= vid_dbl_buf_reset_cnt + 4'd1;
            end
        end

        vid_dbl_buf_reset <= (vid_dbl_buf_reset_cnt != 4'd15);
    end
end


// ==== Double buffer
// ==== saves data from the video generator, pushes it out to the USB packet sender
video_double_buffer u_vid_buffer(
	.ARESET(vid_dbl_buf_reset),
    // Writing interface:
    .WRCLK(combined_pixel_clock), 
    .WRDATA(jpeg_data_delay_3),  
    .PIXEL_DE(jpeg_de_delay[3]), 
    .PIXEL_VS(jpeg_vs), 

    // Reading interface:
    .RDCLK(PHY_CLKOUT), 
    .DATA_READY(buffer_ready), 
    .BUFFER_LENGTH(buffer_length[11:0]), 
    .DATA_COMPLETE(buffer_complete), 

    .LAST(buffer_last),
    .OVERRUN(vid_dbl_buf_overrun), 
    .RDADDR(buffer_addr[10:0]), 
    .RDDATA(buffer_data)
);


endmodule
