`define     HS_EN_DLY   8

module MIPI_CSI_SoftPhy_Rx
(   input           RESET_N
//! MIPI Port: fixed; NONE
,   inout           MIPI_SOFT_CLK_N
,   inout           MIPI_SOFT_CLK_P
,   inout           MIPI_SOFT_D0_N
,   inout           MIPI_SOFT_D0_P
// ,   inout           MIPI_SOFT_D1_N
// ,   inout           MIPI_SOFT_D1_P

,   output          O_PIXEL_CLK
,   output          O_PIXEL_VS
,   output          O_PIXEL_DE
,   output [9:0]    O_PIXEL_DATA

,   output          O_DOUBLE_PIXEL_CLK


// DEBUGGING OUTPUTS
,   output          O_FV_BYTE_CLK // frame valid in byte clock domain
,   output          O_BYTE_COUNT_200K // there were 200,000 active video bytes in this frame
);

localparam  DT_RAW8 = 6'h2A;
localparam  DT_RAW10  = 6'h2B;

wire            byte_clk    /* synthesis syn_keep = 1 */;
wire            lvds_pclk   /* synthesis syn_keep = 1 */;
//
wire [ 1:0]     lp_data0    ;
wire [ 1:0]     lp_data1    ;
wire [ 1:0]     hsrxd_vld   /* synthesis syn_keep = 1 */;
wire [ 7:0]     d0ln_hsrxd  /* synthesis syn_keep = 1 */;
wire [ 7:0]     d1ln_hsrxd  /* synthesis syn_keep = 1 */;
//
reg             byte_ready  ;
reg  [7:0]      byte_d0     ;
reg  [7:0]      byte_d1     ;
reg  [1:0]      lp0_reg_0   =2'b11;
reg  [1:0]      lp0_reg_1   =2'b11;
// reg  [1:0]      lp1_reg_0   =2'b11;
reg             odt_en_msk  = 'b0;
reg             rx_drst_n   =1'b1;
reg             hsrx_en_msk =1'b0;
reg  [5:0]      hsrx_cnt    = 'b0;
reg             reg3to1     =1'b0;
// reg  [1:0]      odt_en      = 'b0;
//
wire            from0to3    = (lp0_reg_1==0)&(lp0_reg_0==3);
wire            from1to0    = (lp0_reg_1==1)&(lp0_reg_0==0);
wire            from1to2    = (lp0_reg_1==1)&(lp0_reg_0==2);
wire            from1to3    = (lp0_reg_1==1)&(lp0_reg_0==3);
wire            from3to1    = (lp0_reg_1==3)&(lp0_reg_0==1);
wire            fromXto3    = (lp0_reg_1!=3)&(lp0_reg_0==3);
wire            from1toX    = (lp0_reg_1==1)&(lp0_reg_0!=1);
wire [ 1:0]     odt_en      = {(lp_data1==0), (lp_data0==0)} & {2{odt_en_msk}};
//
wire            sp_en       /* synthesis syn_keep = 1 */;
wire            lp_en       /* synthesis syn_keep = 1 */;
wire            lp_av_en    /* synthesis syn_keep = 1 */;
wire            ecc_ok      ;
wire [15:0]     wc          ;
wire [ 1:0]     vc          ;
wire [ 5:0]     dt          ;
wire [ 7:0]     ecc         ;
wire            payload_dv  /* synthesis syn_keep = 1 */;
wire [ 7:0]     payload     /* synthesis syn_keep = 1 */;
//
wire            csi_fv      ;
wire            csi_lv      ;
wire [ 9:0]     csi_pixel   ;
//
reg         payload_dv_r= 'b0;
reg  [ 7:0] payload_r   = 'b0;
reg         sp_en_r     = 'b0;
reg         lp_av_en_r  = 'b0;
//


// ******** Debugging stuff ******** //
reg [17:0] total_av_byte_count /* synthesis syn_keep = 1 */;
reg fv_byte_clock /* synthesis syn_keep = 1 */;
wire bytecnt_200k /* synthesis syn_keep = 1 */;

reg ecc_not_ok /* synthesis syn_keep = 1 */;
reg  [5:0]      hsrx_cnt_2    = 'b0;
reg             hsrx_en_msk_2 =1'b0;
wire lp0_00 /* synthesis syn_keep = 1 */;
assign lp0_00 = (lp_data0 == 2'b00);

assign bytecnt_200k = (total_av_byte_count == 18'd200_000); // 500 bytes per line, 400 lines

always @(posedge byte_clk or negedge RESET_N) begin
    if(!RESET_N) begin
        total_av_byte_count <= 18'd0;
        fv_byte_clock <= 1'b0;
    end
    else begin
        if(ecc_ok & sp_en & ({wc,ecc,vc,dt}!=0)) begin
            if(dt == 6'h00) begin // frame start
                total_av_byte_count <= 18'd0;
                fv_byte_clock <= 1'b1;
            end else if(dt == 6'h01) begin
                fv_byte_clock <= 1'b0;
            end
        end
        else if(lp_av_en && ecc_ok) begin
            total_av_byte_count <= total_av_byte_count + wc;
        end
            
    end
end

assign O_FV_BYTE_CLK = fv_byte_clock;
assign O_BYTE_COUNT_200K = bytecnt_200k;

always @(posedge byte_clk or negedge RESET_N) begin
    if(!RESET_N) begin
        ecc_not_ok <= 1'b1;
    end 
    else begin
        // Reset on losing hsrx_en_msk
        if(!hsrx_en_msk_2)
            ecc_not_ok <= 1'b1;
        else begin
            if(ecc_ok)
                ecc_not_ok <= 1'b0;
        end
    end
end

// PLL used to create the pixel clock. It is determined by the bits per pixel, pixels output per clock,
// number of D-PHY lanes, and Rx data mode (8:1 or 16:1)
// fbyte / fpixel = (bits_per_pixel * pixel_per_clock) / (num_dphy_lanes * rx_data_mode)
// For 1 lane, 10 bit raw, 1 pixel per clock, and 8:1 the clock ratio is:
// fbyte / fpixel = (10*1) / (1*8) = 10/8 = 5/4
// Pixel clock is four-fifths of byte clock.

mipi_pixel_pll_60MHz u_pll
(   .reset          (~RESET_N           ) //input reset
,   .clkin          ( byte_clk          ) //input clkin
,   .lock           (                   ) //output lock
,   .clkout0        ( lvds_pclk         ) //output clkout0 - 4/5ths of input clock
,   .clkout1        (O_DOUBLE_PIXEL_CLK ) //output clkout1 - double clkout0
);

MIPI_RX_Advance_Top u_soft_dphy(
    .reset_n(RESET_N), //input reset_n
    .MIPI_CLK_P(MIPI_SOFT_CLK_P), //inout MIPI_CLK_P
    .MIPI_CLK_N(MIPI_SOFT_CLK_N), //inout MIPI_CLK_N
    .lp_clk_out(), //output [1:0] lp_clk_out
    .lp_clk_in(2'b11), //input [1:0] lp_clk_in
    .lp_clk_dir(1'b0), //input lp_clk_dir
    .clk_byte_out(byte_clk), //output clk_byte_out
    // .MIPI_LANE1_P(MIPI_SOFT_D1_P), //inout MIPI_LANE1_P
    // .MIPI_LANE1_N(MIPI_SOFT_D1_N), //inout MIPI_LANE1_N
    // .data_out1(d1ln_hsrxd), //output [7:0] data_out1
    // .lp_data1_out(lp_data1), //output [1:0] lp_data1_out
    // .lp_data1_in(2'b11), //input [1:0] lp_data1_in
    // .lp_data1_dir(1'b0), //input lp_data1_dir
    .MIPI_LANE0_P(MIPI_SOFT_D0_P), //inout MIPI_LANE0_P
    .MIPI_LANE0_N(MIPI_SOFT_D0_N), //inout MIPI_LANE0_N
    .data_out0(d0ln_hsrxd), //output [7:0] data_out0
    .lp_data0_out(lp_data0), //output [1:0] lp_data0_out
    .lp_data0_in(2'b11), //input [1:0] lp_data0_in
    .lp_data0_dir(1'b0), //input lp_data0_dir
    .hs_en(rx_drst_n), //input hs_en
    .clk_term_en(1'b1), //input clk_term_en
    .data_term_en(odt_en[0]), //input data_term_en
    .ready(hsrxd_vld[0]) //output ready
);

always @(posedge byte_clk or negedge RESET_N) begin
    if (~RESET_N)           odt_en_msk  <= 'b0;
    else if (~odt_en_msk)   odt_en_msk  <= from3to1;
    else if (1)             odt_en_msk  <= ~(from1to2|from1to3|fromXto3);
//!______________________________________________________________________________
    if (~RESET_N)           reg3to1     <= 'b0;
    else if (~reg3to1)      reg3to1     <= from3to1;
    else if (1)             reg3to1     <= ~from1toX;
//!______________________________________________________________________________
    if (~RESET_N)           hsrx_cnt    <= 'b0;
    else if (odt_en)        hsrx_cnt    <= `HS_EN_DLY;
    else if (hsrx_cnt>0)    hsrx_cnt    <= hsrx_cnt - 'd1;
//!______________________________________________________________________________
    if (~RESET_N)           hsrx_cnt_2    <= 'b0;
    else if (odt_en)        hsrx_cnt_2    <= 2*(`HS_EN_DLY);
    else if (hsrx_cnt_2>0)    hsrx_cnt_2    <= hsrx_cnt_2 - 'd1;
end

always @(posedge byte_clk or negedge RESET_N) begin
    if(~RESET_N) begin
        rx_drst_n <= 1'b1;
    end
    else begin
        lp0_reg_0   <= lp_data0;
        // lp1_reg_0   <= lp_data1;
        lp0_reg_1   <= lp0_reg_0;
//!______________________________________________________________________________
        rx_drst_n   <= ~(reg3to1&from1to0);
        // rx_drst_n   <= ~from3to1;
//!______________________________________________________________________________
        // odt_en      <= {(lp1_reg_0==0), (lp0_reg_0==0)} & {2{odt_en_msk}};
//!______________________________________________________________________________
        hsrx_en_msk_2 <= (hsrx_cnt_2>0);
//!______________________________________________________________________________
        hsrx_en_msk <= (hsrx_cnt>0);
        // byte_ready  <= hsrx_en_msk & hsrxd_vld[0];
        byte_ready <= hsrx_en_msk & hsrxd_vld[0];
        byte_d0     <= d0ln_hsrxd[7:0];
        // byte_d1     <= d1ln_hsrxd[7:0];
    end
end

CSI2_RX_Top u_csi_rx
(   .I_RSTN         ( RESET_N           ) //input I_RSTN
,   .I_BYTE_CLK     ( byte_clk          ) //input I_BYTE_CLK
,   .I_REF_DT       ( DT_RAW10         ) //input [5:0] I_REF_DT
,   .I_READY        ( byte_ready        ) //input I_READY
,   .I_DATA0        ( byte_d0           ) //input [7:0] I_DATA0
// ,   .I_DATA1        ( byte_d1           ) //input [7:0] I_DATA1
,   .O_SP_EN        ( sp_en             ) //output O_SP_EN
,   .O_LP_EN        ( lp_en             ) //output O_LP_EN
,   .O_LP_AV_EN     ( lp_av_en          ) //output O_LP_AV_EN
,   .O_ECC_OK       ( ecc_ok            ) //output O_ECC_OK
,   .O_ECC          ( ecc               ) //output [7:0] O_ECC
,   .O_WC           ( wc                ) //output [15:0] O_WC
,   .O_VC           ( vc                ) //output [1:0] O_VC
,   .O_DT           ( dt                ) //output [5:0] O_DT
,   .O_PAYLOAD_DV   ( payload_dv        ) //output O_PAYLOAD_DV
,   .O_PAYLOAD      ( payload           ) //output [7:0] O_PAYLOAD
);

always @(posedge byte_clk) begin
    sp_en_r         <= ecc_ok & sp_en & ({wc,ecc,vc,dt}!=0);
//!______________________________________________________________________________
    lp_av_en_r      <= ecc_ok & lp_av_en;
//!______________________________________________________________________________
    payload_dv_r    <= payload_dv;
//!______________________________________________________________________________
    payload_r       <= payload;
end


MIPI_Byte_to_Pixel_Converter_Top u_b2p
(   .I_RSTN         ( RESET_N           ) //input I_RSTN
,   .I_BYTE_CLK     ( byte_clk          ) //input I_BYTE_CLK
,   .I_SP_EN        ( sp_en_r           ) //input I_SP_EN
,   .I_LP_AV_EN     ( lp_av_en_r        ) //input I_LP_AV_EN
,   .I_DT           ( dt                ) //input [5:0] I_DT
,   .I_WC           ( wc                ) //input [15:0] I_WC
,   .I_PAYLOAD_DV   ( payload_dv        ) //input I_PAYLOAD_DV
,   .I_PAYLOAD      ( payload           ) //input [7:0] I_PAYLOAD
,   .I_PIXEL_CLK    ( lvds_pclk         ) //input I_PIXEL_CLK
,   .O_FV           ( csi_fv            ) //output O_FV
,   .O_LV           ( csi_lv            ) //output O_LV
,   .O_PIXEL        ( csi_pixel         ) //output [9:0] O_PIXEL
);

assign O_PIXEL_CLK = lvds_pclk;
assign O_PIXEL_VS = csi_fv;
assign O_PIXEL_DE = csi_lv;
assign O_PIXEL_DATA = csi_pixel;

endmodule
