/******************************************************************************
Copyright 2022 GOWIN SEMICONDUCTOR CORPORATION

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

The Software is used with products manufacturered by GOWIN Semconductor only
unless otherwise authorized by GOWIN Semiconductor.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************************************************************/
`include "usb_defs.v"
`include "uvc_defs.v"

module Top(
    input      CLK_IN, // 24M
    input      RESET,


    inout   camR_mipi_clk_p,
    inout   camR_mipi_clk_n,
    inout   camR_mipi_dat_p,
    inout   camR_mipi_dat_n,

    inout   camR_i2c_sda,
    inout   camR_i2c_scl,
    output  camR_xvclk,
    output  camR_pwm_led,
    input   camR_strobe,

    inout   camL_mipi_clk_p,
    inout   camL_mipi_clk_n,
    inout   camL_mipi_dat_p,
    inout   camL_mipi_dat_n,

    inout   camL_i2c_sda,
    inout   camL_i2c_scl,
    output  camL_xvclk,   
    output  camL_pwm_led,
    input   camL_strobe,

	output [4:0] GPIO,
	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,

    input   mcu_scl,
    inout   mcu_sda

// Manually add ADC pad selection to .cst file
//  USE_ADC_SRC bus1 IOB26 // F2 (p) F1 (n) left camera analog input
//  USE_ADC_SRC bus0 IOT19 // L7 (p) L8 (n) right camera analog input

);

localparam INITIAL_PRE_PULSE = 16'd2250; // 2,250 / 60,000,000 * 8 = 166 us
localparam INITIAL_PULSE_COUNT = 16'd7500; // 7,500 / 60,000,000 * 8 = 1000 us

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        PHY_RESET           ;

wire        pll_locked;

wire        usb_busreset;  //unused
wire        usb_highspeed; //unused
wire        usb_suspend;   //unused
wire        usb_online;    //unused
wire        rxpktval;      //unused
wire [7:0]  usb_txdat;
reg [11:0]  txdat_len;
wire [ 7:0] endpt0_dat;
wire        endpt0_send;
wire [11:0] endpt0_dat_len;
wire        usb_txval;
reg         usb_txcork;
wire        usb_txpop;
wire        usb_txact;
wire [7:0]  usb_rxdat;
wire        usb_rxact;
wire        usb_rxval;
wire        usb_rxrdy;
wire        usb_sof;
reg  [7:0]  rst_cnt;
wire [3:0]  endpt_sel;
wire        setup_active;
wire        setup_val;
wire [7:0]  setup_data;
wire        fclk_960M;
wire        fclk_24M;
wire        fclk_12M;
wire        jpeg_clk;



wire [9:0]  DESCROM_RADDR       ;
wire [7:0]  DESCROM_RDAT        ;
wire [9:0]  DESC_DEV_ADDR       ;
wire [7:0]  DESC_DEV_LEN        ;
wire [9:0]  DESC_QUAL_ADDR      ;
wire [7:0]  DESC_QUAL_LEN       ;
wire [9:0]  DESC_FSCFG_ADDR     ;
wire [7:0]  DESC_FSCFG_LEN      ;
wire [9:0]  DESC_HSCFG_ADDR     ;
wire [7:0]  DESC_HSCFG_LEN      ;
wire [9:0]  DESC_OSCFG_ADDR     ;
wire [9:0]  DESC_STRLANG_ADDR   ;
wire [9:0]  DESC_STRVENDOR_ADDR ;
wire [7:0]  DESC_STRVENDOR_LEN  ;
wire [9:0]  DESC_STRPRODUCT_ADDR;
wire [7:0]  DESC_STRPRODUCT_LEN ;
wire [9:0]  DESC_STRSERIAL_ADDR ;
wire [7:0]  DESC_STRSERIAL_LEN  ;
wire        DESCROM_HAVE_STRINGS;

wire        RESET_IN;
reg         reset_usb;

wire RESET_N = ~RESET;

// communication between I2C (to controlling MCU) and cameras
wire        cam_write_start;
wire [15:0] cam_write_addr;
wire [7:0]  cam_write_value;
wire        cam_write_busy;
wire        cam_write_busy_L;
wire        cam_write_busy_R;
wire        camL_config_done;
wire        camR_config_done;

// and from I2C to IR pulse generators
wire [15:0] new_pre_pulse;
wire [15:0] new_pulse_length;
wire pre_pulse_update;
wire pulse_length_update;
// wire [15:0] new_pwm_duty;
// wire        pwm_update;

// wire [15:0] monitor_data_in;
// wire        monitor_arm;
// wire        monitor_trig;
// wire        monitor_capture_done;
// wire  [8:0] monitor_trig_point;
// wire [15:0] monitor_data_out;
// wire        monitor_read_enable;
// wire  [7:0] ila_txdat;
// wire [11:0] ila_txdat_len;
// wire        ila_txcork;

/****** DEBUGGING WIRES ********/
wire        mipi_soft_fv_byte_clock; 
wire        mipi_soft_byte_count_200k;
wire        mipi_soft_odt_en;
wire        mipi_soft_badpacket;

wire        mipi_hard_fv_byte_clock; 
wire        mipi_hard_byte_count_200k;
wire        mipi_hard_byte_ready;
reg [1:0]   mipi_hard_byte_ready_cdc;

wire        mipi_hard_byte_clk;
wire        mipi_hard_drst_n;
wire [1:0]  mipi_hard_lp0;
wire [7:0]  mipi_hard_byte_d0;

wire        jpeg_misalign; // signal that jpeg done occurred in the middle of a frame

reg [1:0]   jpeg_mis_clkout = 2'b00; // cdc for resets on cam i2c controllers

wire camLR_pix_clk; // combined pixel clock for output of line combiner
wire [7:0] camLR_data;
wire camLR_de;
wire camLR_vsync;

wire camR_vsync;
wire camR_pix_clk;
wire camR_de;
wire [9:0] camR_data;

wire CLK_OUT /* synthesis syn_keep=1 */;

DCE u_clkin_DCE
(
.CLKIN(CLK_IN),
.CLKOUT(CLK_OUT),
.CE(1'b1)
);

MIPI_CSI_HardPhy_Rx
u_mipi_hard
(
    .RESET_N(RESET_N),
    .CAM_INIT_DONE(camR_config_done),
//    .DRP_CLK(CLK_OUT),
    .MIPI_HARD_CLK_P(camR_mipi_clk_p),
    .MIPI_HARD_CLK_N(camR_mipi_clk_n),
    .MIPI_HARD_D0_P(camR_mipi_dat_p),
    .MIPI_HARD_D0_N(camR_mipi_dat_n),
    
    .O_PIXEL_VS(camR_vsync),
    .O_PIXEL_CLK(camR_pix_clk),
    .O_PIXEL_DE(camR_de),
    .O_PIXEL_DATA(camR_data),
    .O_DOUBLE_PIXEL_CLK(camLR_pix_clk)

,   .O_FV_BYTE_CLK(mipi_hard_fv_byte_clock)
,   .O_BYTE_COUNT_200K(mipi_hard_byte_count_200k)
,   .O_BYTE_READY(mipi_hard_byte_ready)

,   .O_BYTE_CLK(mipi_hard_byte_clk)
,   .O_RX_DRST_N(mipi_hard_drst_n)
,   .O_LP0(mipi_hard_lp0)
,   .O_BYTE_D0(mipi_hard_byte_d0)
);

wire        camR_scl_temp;
wire        camR_sda_output;
wire        camR_sda_input;
wire        camR_sda_ioen;

assign camR_xvclk = CLK_OUT;

IOBUF u_iobuf_camR_sda(
    .IO(camR_i2c_sda),
    .I(camR_sda_output),
    .O(camR_sda_input),
    .OEN(camR_sda_output | camR_sda_ioen)
);
IOBUF u_iobuf_camR_scl(
    .IO(camR_i2c_scl),
    .I(camR_scl_temp),
    .O(),
    .OEN(camR_scl_temp)
);

oc0ta1b_controller u_cam_init_camR
(	.clk            ( CLK_OUT            )   // 24Mhz clock signal
,   .rstn           ( RESET_N  && ~(jpeg_mis_clkout[1]) )
,   .config_finished( camR_config_done  )   // Flag to indicate that the configuration is finished
,   .sioc           ( camR_scl_temp     )   // SCCB interface - clock signal
,   .siod           ( camR_sda_output   )   // SCCB interface - data signal
,   .siod_ioen      ( camR_sda_ioen     )

,   .ovw_send       ( cam_write_start   )   // trigger a new command to sender
,   .ovw_reg_addr   ( cam_write_addr    )
,   .ovw_reg_value  ( cam_write_value   )
,   .ovw_busy       ( cam_write_busy_R  )
);


wire camL_vsync;
wire camL_pix_clk;
wire camL_de;
wire [9:0] camL_data;
MIPI_CSI_SoftPhy_Rx
u_mipi_soft
(
    .RESET_N(RESET_N),
    .CAM_INIT_DONE(camL_config_done),
//    .DRP_CLK(CLK_OUT),
    .MIPI_SOFT_CLK_P(camL_mipi_clk_p),
    .MIPI_SOFT_CLK_N(camL_mipi_clk_n),
    .MIPI_SOFT_D0_P(camL_mipi_dat_p),
    .MIPI_SOFT_D0_N(camL_mipi_dat_n),
    
    .O_PIXEL_VS(camL_vsync),
    .O_PIXEL_CLK(camL_pix_clk),
    .O_PIXEL_DE(camL_de),
    .O_PIXEL_DATA(camL_data)

,   .O_FV_BYTE_CLK(mipi_soft_fv_byte_clock)
,   .O_BYTE_COUNT_200K(mipi_soft_byte_count_200k)
,   .O_ODT_EN(mipi_soft_odt_en)
,   .O_BADPACKET(mipi_soft_badpacket)
);


wire        camL_scl_temp;
wire        camL_sda_output;
wire        camL_sda_input;
wire        camL_sda_ioen;

assign camL_xvclk = CLK_OUT;

IOBUF u_iobuf_camL_sda(
    .IO(camL_i2c_sda),
    .I(camL_sda_output),
    .O(camL_sda_input),
    .OEN(camL_sda_output | camL_sda_ioen)
);
IOBUF u_iobuf_camL_scl(
    .IO(camL_i2c_scl),
    .I(camL_scl_temp),
    .O(),
    .OEN(camL_scl_temp)
);

oc0ta1b_controller u_cam_init_camL
(	.clk            ( CLK_OUT            )   // 24Mhz clock signal
,   .rstn           ( RESET_N  && ~(jpeg_mis_clkout[1]))
,   .config_finished( camL_config_done  )   // Flag to indicate that the configuration is finished
,   .sioc           ( camL_scl_temp     )   // SCCB interface - clock signal
,   .siod           ( camL_sda_output   )   // SCCB interface - data signal
,   .siod_ioen      ( camL_sda_ioen     )

,   .ovw_send       ( cam_write_start   )   // trigger a new command to sender
,   .ovw_reg_addr   ( cam_write_addr    )
,   .ovw_reg_value  ( cam_write_value   )
,   .ovw_busy       ( cam_write_busy_L  )
);

wire camR_empty;
wire camL_empty;
wire camR_activated;
wire camL_activated;
wire camLR_outputting;

line_combiner u_line_comb(
    .ARESET(RESET || (!camR_config_done) || (!camL_config_done)),

    .CAM1_CLK(camL_pix_clk),
    .CAM1_DATA(camL_data[9:2]),
    .CAM1_VS(camL_vsync),
    .CAM1_DE(camL_de),

    .CAM2_CLK(camR_pix_clk),
    .CAM2_DATA(camR_data[9:2]),
    .CAM2_VS(camR_vsync),
    .CAM2_DE(camR_de),

    .COMBINED_CLK(jpeg_clk),
    .COMBINED_DATA(camLR_data),
    .COMBINED_VS(camLR_vsync),
    .COMBINED_DE(camLR_de)

,   .CAM1_EMPTY(camL_empty)
,   .CAM2_EMPTY(camR_empty)
,   .CAM1_ACTIVATED(camL_activated)
,   .CAM2_ACTIVATED(camR_activated)
,   .COMBINED_OUTPUTTING(camLR_outputting)
);


//==============================================================
//======PLL 

    Gowin_PLL u_pll_usb(
        .lock(pll_locked), //output lock
        .clkout0(PHY_CLKOUT), //output clkout0 (60 MHz)
        .clkout1(fclk_960M), //output clkout1 (960 MHz)
        .clkout2(jpeg_clk), // output clkout2 (80 MHz)
//        .clkin(fclk_24M) //input clkin
        .clkin(CLK_OUT) //input clkin
,       .mdclk(CLK_OUT)
    );

//==============================================================
//======Reset
assign RESET_IN = rst_cnt<32;
always@(posedge PHY_CLKOUT, negedge RESET_N) begin
    if (!RESET_N) begin
        rst_cnt <= 8'd0;
    end
    else if (rst_cnt <= 32) begin
        rst_cnt <= rst_cnt + 8'd1;
    end
end

//==============================================================
//======UVC Frame Data

//reg  [11:0] txdat_len;
//wire [11:0] uvc_txlen;
//wire [7:0] frame_data;

assign usb_txdat = (endpt_sel == 4'd0) ? (endpt0_dat[7:0]) : (endpt_sel == 4'd2) ? fifo_rdat[7:0] : /*(endpt_sel == 4'd3) ? ila_txdat :*/ 8'd0;
assign usb_txval = (endpt_sel == 4'd0) ? (endpt0_send) : 1'b0;

wire fifo_afull;
wire fifo_aempty;
wire [7:0]  fifo_rdat;
wire [11:0] fifo_wnum;

wire jpeg_de;
wire jpeg_done;
wire [7:0] jpeg_data;
jpeg_top
#(
    .IMG_SIZE_X(`WIDTH), .IMG_SIZE_Y(`HEIGHT), .QUANT_Q(80)
)
u_jpeg
(
    .CLK(jpeg_clk),
    .ARESET(RESET || jpeg_misalign /*|| (!camR_config_done) || (!camL_config_done)*/),
    .IMG_DATA(camLR_data),
    .IMG_DATA_VALID(camLR_de),
    .IMG_DATA_VSYNC(camLR_vsync),
    .DATA_OUT(jpeg_data),
    .DATA_OUT_VALID(jpeg_de),
    .DATA_OUT_END(jpeg_done)
);

reg jpeg_done_stretched;
reg [7:0] jpeg_stretcher;
always @(posedge jpeg_clk or posedge RESET) begin
    if(RESET) begin
        jpeg_done_stretched <= 1'b0;
        jpeg_stretcher <= 8'h00;
    end
    else begin
        if(jpeg_done_stretched) begin
            if((!jpeg_done) && (jpeg_stretcher != 8'h00))
                jpeg_stretcher <= jpeg_stretcher - 'd1;
            if(jpeg_stretcher == 8'h00)
                jpeg_done_stretched <= 1'b0;
        end
        else begin
            if(jpeg_done) begin
                jpeg_stretcher <= 8'hFF;
                jpeg_done_stretched <= 1'b1;
            end
        end
    end
end

assign jpeg_misalign = (camLR_vsync && jpeg_done_stretched);

always @(posedge CLK_OUT) begin
    jpeg_mis_clkout <= {jpeg_mis_clkout[0], jpeg_misalign};
end

reg jpeg_done_phyclk; // cross jpeg done signal across clock domain to USB PHY clk
reg jpeg_done_cdc;
always @(posedge PHY_CLKOUT) begin
    jpeg_done_cdc <= jpeg_done_stretched;
    jpeg_done_phyclk <= jpeg_done_cdc;
end

reg stream_enable_r, stream_enable;

isoc_video_buffer u_vid_buf
(
    .ARESET(RESET),
    .JPEG_CLK(jpeg_clk),
    .JPEG_DE(jpeg_de),
    .JPEG_DONE(jpeg_done),
    .JPEG_DATA(jpeg_data),
    .STREAM_ENABLE(stream_enable),

    .USB_CLK(PHY_CLKOUT),
    .TXACT(usb_txact&(endpt_sel==4'd2)),
    .TXPOP(usb_txact&usb_txpop&(endpt_sel==4'd2)),
    .USB_SOF(usb_sof),
    .TXDAT(fifo_rdat),
    .TXCORK(),
    .TXDAT_LEN(fifo_wnum)
);

always @(posedge PHY_CLKOUT) begin
    mipi_hard_byte_ready_cdc <= {mipi_hard_byte_ready_cdc[0], mipi_hard_byte_ready};
end

// camR_saw_vsync
// camR_write_enable



//assign GPIO[2] = mipi_soft_byte_count_200k & mipi_hard_byte_count_200k;
//assign GPIO[2] = mipi_hard_byte_ready_cdc[1];

// assign GPIO[3] = camLR_de;
// assign GPIO[3] = camL_empty;
// assign GPIO[4] = camLR_vsync;
// assign GPIO[4] = camR_empty;


always@(posedge PHY_CLKOUT, posedge RESET_IN) begin
    if (RESET_IN) begin
        txdat_len <= 12'd34;
        usb_txcork <= 1'b0;
    end
    else if (usb_txact) begin
//        if (endpt_sel == 0) begin
//            txdat_len <= 12'd34; //Read FIFO Data Bytes Count
//            txdat_len <= 12'd0; //Read FIFO Data Bytes Count
//        end
//        else begin
            txdat_len <= txdat_len;
//        end
    end
    else begin
        if (endpt_sel == 0) begin
            txdat_len <= endpt0_dat_len; //set by usb control handler
            usb_txcork <= 1'b0;
        end
        //else if ((endpt_sel == 4'd2)&&(in_cnt == 16'd0)) begin
        else if (endpt_sel == 4'd2) begin
            usb_txcork <= 1'b0;
            txdat_len <= fifo_wnum;
/*
            //if (fifo_aempty&frame_dval) begin
            if (fifo_afull) begin
                txdat_len <= 12'd1024;
                //txdat_len <= 12'd52;//fifo_wnum;
                //txdat_len <= fifo_wnum;
                usb_txcork <= 1'b0;
            end
            else begin
                if (frame_dval) begin
                    usb_txcork <= 1'b0; //1'b1;
                    txdat_len <= 12'd0;
                end
                else if (fifo_empty) begin
                    usb_txcork <= 1'b0; //1'b1;
                    txdat_len <= 12'd0;
                end
                else if (fifo_aempty) begin
                    usb_txcork <= 1'b0;
                    txdat_len <= fifo_wnum;
                end
                else begin
                    usb_txcork <= 1'b0;
                    txdat_len <= 12'd1024;//fifo_wnum;
                end
            end
*/
        end
        // else if (endpt_sel == 4'd3) begin
        //     txdat_len <= ila_txdat_len;
        // end
//        else if (endpt_sel == 4'd4) begin
//            txdat_len = cdc_txdat_len;
//        end
        else begin
            txdat_len <= 12'd0;
            usb_txcork <= 1'b1;
        end
    end
end

`define ENDPT1_ISO_HB
`define ENDPT2_ISO_HB
`define ENDPT3_ISO_HB
`define ENDPT4_ISO_HB
`define ENDPT5_ISO_HB
`define ENDPT6_ISO_HB
`define ENDPT7_ISO_HB
`define ENDPT8_ISO_HB
`define ENDPT9_ISO_HB
`define ENDPT10_ISO_HB
`define ENDPT11_ISO_HB
`define ENDPT12_ISO_HB
`define ENDPT13_ISO_HB
`define ENDPT14_ISO_HB
`define ENDPT15_ISO_HB
//`define MFRAME_PACKETS3
`define HSSUPPORT
//`define MFRAME_PACKETS2
reg [3:0] iso_pid_data;
always @(posedge PHY_CLKOUT /*or posedge s_reset*/) begin
    if(RESET_IN) begin
        `ifdef HSSUPPORT
            `ifdef MFRAME_PACKETS3
                iso_pid_data <= 4'b0111;//DATA2
            `elsif MFRAME_PACKETS2
                iso_pid_data <= 4'b1011;//DATA1
            `else
                iso_pid_data <= 4'b0011;//DATA0
            `endif
        `else
            iso_pid_data <= 4'b0011;//DATA0
        `endif
    end
    else begin
        `ifdef HSSUPPORT
            `ifdef MFRAME_PACKETS3
                if (usb_sof) begin
                    if (fifo_afull) begin
                        iso_pid_data <= 4'b0111;//DATA2
                    end
                    else if (fifo_aempty) begin
                        iso_pid_data <= 4'b0011;//DATA0
                    end
                    else begin
                        iso_pid_data <= 4'b1011;//DATA1
                    end
                    //iso_pid_data <= 4'b0111;//DATA2
                end
                else if (txact_fall&&(endpt_sel==4'd2)) begin
                    iso_pid_data <= (iso_pid_data == 4'b0111) ? 4'b1011 : ((iso_pid_data == 4'b1011) ? 4'b0011 : iso_pid_data);//DATA2(0111) -> DATA1(1011) -> DATA0(0011)
                end
            `elsif MFRAME_PACKETS2
                if (usb_sof) begin
                    //if (fifo_afull) begin
                    //    iso_pid_data <= 4'b0111;//DATA2
                    //end
                    //else if (fifo_aempty) begin
                    //    iso_pid_data <= 4'b0011;//DATA0
                    //end
                    //else begin
                    //    iso_pid_data <= 4'b1011;//DATA1
                    //end
                    iso_pid_data <= 4'b0111;//DATA2
                end
                else if (txact_fall&&(endpt_sel==4'd2)) begin
                    iso_pid_data <= (iso_pid_data == 4'b1011) ? 4'b0011 : iso_pid_data;//DATA1(1011) -> DATA0(0011)
                end
            `else
                iso_pid_data <= 4'b0011;//DATA0
            `endif
        `else
            iso_pid_data <= 4'b0011;//DATA0
        `endif
    end
end

//==============================================================
//======Interface Setting
wire [7:0] interface_alter_i;
wire [7:0] interface_alter_o;
wire [7:0] interface_sel;
wire       interface_update;

reg [7:0] interface0_alter;
reg [7:0] interface1_alter;
assign interface_alter_i = (interface_sel == 0) ?  interface0_alter :
                           (interface_sel == 1) ?  interface1_alter : 8'd0;
always@(posedge PHY_CLKOUT, posedge RESET_IN   ) begin
    if (RESET_IN) begin
        interface0_alter <= 'd0;
        interface1_alter <= 'd0;
    end
    else begin
        if (interface_update) begin
            if (interface_sel == 0) begin
                interface0_alter <= interface_alter_o;
            end
            else if (interface_sel == 1) begin
                interface1_alter <= interface_alter_o;
            end
        end
    end
end

always @(posedge jpeg_clk or posedge RESET) begin
    if(RESET) begin
        stream_enable_r <= 1'b0;
        stream_enable <= 1'b0;
    end else begin
        stream_enable_r <= (interface1_alter == 8'h01);
        stream_enable <= stream_enable_r;
    end
end

    USB_Device_Controller_Top u_usb_device_controller_top (
             .clk_i                 (PHY_CLKOUT         )
            ,.reset_i               (RESET_IN  || reset_usb )
            ,.usbrst_o              (usb_busreset        )
            ,.highspeed_o           (usb_highspeed       )
            ,.suspend_o             (usb_suspend         )
            ,.online_o              (usb_online          )
            //,.iso_pid_i           (usb_iso_pid           )//
            ,.txdat_i             (usb_txdat           )//
            ,.txval_i             (usb_txval           )//endpt0_send
            ,.txdat_len_i         (txdat_len           )//
            ,.txiso_pid_i         (iso_pid_data        )//
//            ,.txcork_i            (endpt_sel == 'd0 ? 1'b0 : (endpt_sel == 'd2 ? usb_txcork : cdc_txcork))//usb_txcork
            ,.txcork_i            (endpt_sel == 'd0 ? (1'b0) : (endpt_sel == 'd2 ? usb_txcork : 1'b1)) // (endpt_sel == 'd3: ila_txcork : 1'b1)))//usb_txcork
//            ,.txcork_i            (usb_txcork          )//usb_txcork
            ,.txpop_o             (usb_txpop           )
            ,.txact_o             (usb_txact           )
            ,.rxdat_o             (usb_rxdat           )
            ,.rxval_o             (usb_rxval           )
            ,.rxact_o             (usb_rxact           )
            ,.rxrdy_i             (1'b1                )
            ,.rxpktval_o          (rxpktval            )
            ,.setup_o             (setup_active        )
            ,.endpt_o             (endpt_sel           )
            ,.sof_o               (usb_sof             )
            ,.inf_alter_i         (interface_alter_i   )
            ,.inf_alter_o         (interface_alter_o   )
            ,.inf_sel_o           (interface_sel       )
            ,.inf_set_o           (interface_update    )

            ,.descrom_rdata_i       (DESCROM_RDAT        )
            ,.descrom_raddr_o       (DESCROM_RADDR       )
            ,.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_len_i     (16'd0               )
            ,.desc_bos_addr_i       (16'd0               )
            ,.desc_bos_len_i        (16'd0               )
            ,.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
#(

//         .VENDORID    (16'h33AA)//0403   08bb
//        ,.PRODUCTID   (16'h0200)//6010   27c6
//        ,.VERSIONBCD  (16'h0200)
         .VENDORID    (16'h35BD)
        ,.PRODUCTID   (16'h0202)
        ,.VERSIONBCD  (16'h0100)
        ,.HSSUPPORT   (1)
        ,.SELFPOWERED (0)
         ,.VENDORSTR   ("Bigscreen")
         ,.VENDORSTR_LEN (9)
         ,.PRODUCTSTR  ("Bigeye")
         ,.PRODUCTSTR_LEN (6)
)
u_usb_desc (
         .CLK                    (PHY_CLKOUT          )
        ,.RESET                  (RESET_IN   || reset_usb          )
        ,.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 || reset_usb    )
        ,.fclk_i           (fclk_960M     )
        ,.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_control_handler u_ctrl_handler(
    .RESET_IN(RESET_IN  || reset_usb),
    .PHY_CLKOUT(PHY_CLKOUT),
    .SETUP_ACTIVE(setup_active),
    .USB_RXACT(usb_rxact),
    .USB_RXVAL(usb_rxval),
    .USB_RXDAT(usb_rxdat),
    .USB_TXACT(usb_txact),
    .USB_TXPOP(usb_txpop),
    .USB_ENDPT_SEL(endpt_sel),
    .ENDPT0_SEND(endpt0_send),
    .ENDPT0_DAT(endpt0_dat),
    .ENDPT0_DAT_LEN(endpt0_dat_len)
);


// assign monitor_trig = camLR_vsync;
// big_ila_top u_big_ila
// (
//     .RESET(RESET_IN)
// ,   .CAPTURE_CLK(jpeg_clk)
// ,   .TRIG(monitor_trig)
// ,   .camLR_vsync(camLR_vsync)
// ,   .camLR_de(camLR_de)
// ,   .camL_empty(camL_empty)
// ,   .camR_empty(camR_empty)
// ,   .camLR_outputting(camLR_outputting)

// ,   .USB_CLK(PHY_CLKOUT)
// ,   .RXACT(usb_rxact && (endpt_sel == 4'd3))
// ,   .RXVAL(usb_rxval && (endpt_sel == 4'd3))
// ,   .RXDAT(usb_rxdat)
// ,   .TXACT(usb_txact && (endpt_sel == 4'd3))
// ,   .TXPOP(usb_txpop && (endpt_sel == 4'd3))
// ,   .TXDAT(ila_txdat)
// ,   .TXDAT_LEN(ila_txdat_len)
// ,   .TXCORK(ila_txcork)
// );


reg [15:0] pre_pulse_count;
reg [15:0] pulse_length_count;

always @(posedge PHY_CLKOUT or posedge RESET) begin
    if(RESET) begin
        // Default settings:
        // Clock is 60MHz (phy clock) but there's a /8 divider on the
        // pulse generator
        // Default setting is 1ms long pulse (so total duty cycle is 1ms / 11.111ms = 9.01%)
        // with a 300us pre-pulse delay
        pre_pulse_count <= INITIAL_PRE_PULSE; // 2,250 / 60,000,000 * 8 = 166 us
        pulse_length_count <= INITIAL_PULSE_COUNT; // 7,500 / 60,000,000 * 8 = 1000 us
    end else begin
        if(pre_pulse_update) pre_pulse_count <= new_pre_pulse;
        if(pulse_length_update) pulse_length_count <= new_pulse_length;
    end
end

irled_pulse
#(.CLK_DIV(8))
u_pulse_left
(
    .CLK(PHY_CLKOUT)
,   .RESET(RESET)
,   .STROBE(camL_strobe)
,   .PRE_PULSE_COUNT(pre_pulse_count)
,   .PULSE_LENGTH_COUNT(pulse_length_count)
,   .PULSE(camL_pwm_led)
);

irled_pulse 
#(
    .CLK_DIV(8)
)
u_pulse_right
(
    .CLK(PHY_CLKOUT)
,   .RESET(RESET)
,   .STROBE(camR_strobe)
,   .PRE_PULSE_COUNT(pre_pulse_count)
,   .PULSE_LENGTH_COUNT(pulse_length_count)
,   .PULSE(camR_pwm_led)
);

wire        adcready;
wire        adcrequest;
wire        adcchannel;
wire        samplezero;
wire [13:0] adcdata;

adcreq
#(
    .CLK_FREQ_MHZ(60)
)
u_adcreq
(
    .CLK(PHY_CLKOUT)
,   .RESET(RESET)
,   .PULSE(camL_pwm_led)

,   .ADC_REQ(adcrequest)
,   .ADC_READY(adcready)
,   .ADC_CHAN(adcchannel)
,   .SAMPLE_ZERO(samplezero)
);

Gowin_ADC u_adc(
    .adcrdy(adcready) //output adcrdy
,   .adcvalue(adcdata) //output [13:0] adcvalue
,   .mdrp_rdata() //output [7:0] mdrp_rdata
,   .vsenctl(adcchannel ? 3'b000 : 3'b001) //input [2:0] vsenctl (3'b000 = glo_left (BANK 0/6/7), 3'b001 = glo_right (BANK 2/3/4/5)
,   .adcen(1'b1) //input adcen
,   .clk(CLK_OUT) //input clk
,   .drstn(~RESET) //input drstn
,   .adcreqi(adcrequest) //input adcreqi
,   .adcmode(1'b1) //input adcmode 1 = voltage mode, 0 = temperature mode
,   .mdrp_clk(1'b0) //input mdrp_clk
,   .mdrp_wdata(8'h0) //input [7:0] mdrp_wdata
,   .mdrp_a_inc(1'b0) //input mdrp_a_inc
,   .mdrp_opcode(2'b00) //input [1:0] mdrp_opcode
);

wire [13:0] i_left_run;
wire [13:0] i_right_run;
wire [13:0] i_left_idle;
wire [13:0] i_right_idle;
wire [7:0]  i_errors;

wire [13:0] run_limit;
wire [13:0] idle_limit;

current_monitor u_imon
(
    .CLK(PHY_CLKOUT)
,   .RESET(RESET)
,   .ADC_READY(adcready)
,   .ADC_DATA(adcdata)
,   .ADC_CHAN(adcchannel)
,   .SAMPLE_ZERO(samplezero)

,   .IR_LEFT_RUN(i_left_run)
,   .IR_LEFT_IDLE(i_left_idle)
,   .IR_RIGHT_RUN(i_right_run)
,   .IR_RIGHT_IDLE(i_right_idle)

,   .CONSECUTIVE_ERROR_LIMIT(4'd4)

,   .IR_LEFT_RUN_ERROR(i_errors[0])
,   .IR_RIGHT_RUN_ERROR(i_errors[1])
,   .IR_LEFT_IDLE_ERROR(i_errors[2])
,   .IR_RIGHT_IDLE_ERROR(i_errors[3])

,   .IR_LEFT_RUN_ERROR_LATCH(i_errors[4])
,   .IR_RIGHT_RUN_ERROR_LATCH(i_errors[5])
,   .IR_LEFT_IDLE_ERROR_LATCH(i_errors[6])
,   .IR_RIGHT_IDLE_ERROR_LATCH(i_errors[7])

,   .RUN_LIMIT(run_limit)
,   .IDLE_LIMIT(idle_limit)
);

/*
reg [15:0] pwm_dc;

always @(posedge PHY_CLKOUT or posedge RESET) begin
    if(RESET) begin
        pwm_dc <= 16'd600; // default of 10%    
    end
    else begin
        if(pwm_update)
            pwm_dc <= new_pwm_duty;
    end
end

pwm_generator #(.RELOAD_VALUE(16'd5999)) // 60MHz / 6000 = 10kHz
u_pwm
(
    .CLK(PHY_CLKOUT),
    .RESET(RESET),
    .DUTY_CYCLE(pwm_dc), 
    .PWM(cam_led_pwm)
);
*/

// ---------- I2C external control ----------
assign cam_write_busy = cam_write_busy_L & cam_write_busy_R; // both cams must be writing for this ack signal to go high

wire        ctrl_sda_input;
wire        ctrl_sda_output;


IOBUF u_iobuf_ctrl_sda(
    .IO(mcu_sda),
    .I(ctrl_sda_output),
    .O(ctrl_sda_input),
    .OEN(ctrl_sda_output)
);

i2c_device_top u_i2c_ctrl(
    .CLK(PHY_CLKOUT)
,   .RST(RESET)
,   .SDA_I(ctrl_sda_input)
,   .SDA_O(ctrl_sda_output)
,   .SCL(mcu_scl)

    // control outputs
    // Camera I2C commands
,   .CAM_REG_ADDR(cam_write_addr)
,   .CAM_REG_VALUE(cam_write_value)
,   .CAM_REG_WRITE_START(cam_write_start)
,   .CAM_REG_WRITE_IN_PROGRESS(cam_write_busy)

    // IR LED pulse control
,   .PRE_PULSE_COUNT(new_pre_pulse)
,   .PULSE_LENGTH_COUNT(new_pulse_length)
,   .PRE_PULSE_UPDATE(pre_pulse_update)
,   .PULSE_LENGTH_UPDATE(pulse_length_update)

    // IR LED measurements
,   .I_LEFT_RUN(i_left_run)
,   .I_RIGHT_RUN(i_right_run)
,   .I_LEFT_IDLE(i_left_idle)
,   .I_RIGHT_IDLE(i_right_idle)
,   .I_ERRORS(i_errors)

    // LED current limits
,   .RUN_CURRENT_LIMIT(run_limit)
,   .IDLE_CURRENT_LIMIT(idle_limit)

    // Debug inputs/outputs
,   .RESET_USB(reset_usb)
);

reg jpeg_done_phyclk_r;
wire jpeg_done_phyclk_rising = (jpeg_done_phyclk) && (!jpeg_done_phyclk_r);

reg [7:0] jpeg_done_count;
always @(posedge PHY_CLKOUT or posedge RESET) begin
    if(RESET) begin
        reset_usb <= 1'b1; // start with USB held in reset
        jpeg_done_phyclk_r <= 1'b0;
        jpeg_done_count <= 8'd0;
    end 
    else begin
        jpeg_done_phyclk_r <= jpeg_done_phyclk;
        // Remove USB reset once we got frames coming in
        // this will ensure a non-eyetracking Beyond doesn't load with
        // a USB device.
        if(reset_usb) begin
            // observe a few edges of jpeg_done_phyclk to make sure
          if(jpeg_done_phyclk_rising)
              jpeg_done_count <= jpeg_done_count + 8'd1;
          if(jpeg_done_count >= 40)
              reset_usb <= 1'b0;


            // Also can be released by debug I2C
            // TODO: debug I2C register writes
        end
    end
end

assign GPIO[0] = camR_vsync;
assign GPIO[1] = camL_vsync;
assign GPIO[2] = mipi_soft_byte_count_200k;
assign GPIO[3] = camR_empty;
assign GPIO[4] = camL_empty;

endmodule
