`define     HS_EN_DLY   8

module MIPI_CSI_HardPhy_Rx
(   input           RESET_N
,   input           OSC_50M
//! MIPI Port: fixed; NONE
,   inout           MIPI_HARD_CLK_N
,   inout           MIPI_HARD_CLK_P
,   inout           MIPI_HARD_D0_N
,   inout           MIPI_HARD_D0_P
,   inout           MIPI_HARD_D1_N
,   inout           MIPI_HARD_D1_P

,   output          O_PIXEL_CLK
,   output          O_PIXEL_VS
,   output          O_PIXEL_DE
,   output [7:0]    O_PIXEL_DATA
);

localparam  DT_RGB888 = 6'h2A;

wire            byte_clk    /* synthesis syn_keep = 1 */;
wire            lvds_eclk   /* 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 [ 1:0]     payload_dv  /* synthesis syn_keep = 1 */;
wire [15:0]     payload     /* synthesis syn_keep = 1 */;
//
wire            csi_fv      ;
wire            csi_lv      ;
wire [ 7:0]     csi_pixel   ;
//
reg  [ 1:0] payload_dv_r= 'b0;
reg  [15:0] payload_r   = 'b0;
reg         sp_en_r     = 'b0;
reg         lp_av_en_r  = 'b0;
//

// PLL used to double the byte clock (which is synchronous with the MIPI D-PHY lanes)
// to create the pixel clock. This is for 2-lane D-PHY. Single lane will not need this PLL.
mipi_pixel_pll u_pll
(   .reset          (~RESET_N           ) //input reset
,   .clkin          ( byte_clk          ) //input clkin
,   .lock           (                   ) //output lock
,   .clkout0        ( lvds_eclk         ) //output clkout0
,   .clkout1        ( lvds_pclk         ) //output clkout1
);

Gowin_MIPI_DPHY u_hard_dphy
(   .rx_drst_n      ( rx_drst_n         ) //input rx_drst_n
,   .ck_n           ( MIPI_HARD_CLK_N   ) //inout ck_n
,   .ck_p           ( MIPI_HARD_CLK_P   ) //inout ck_p
,   .d0_n           ( MIPI_HARD_D0_N    ) //inout d0_n
,   .d0_p           ( MIPI_HARD_D0_P    ) //inout d0_p
,   .d1_n           ( MIPI_HARD_D1_N    ) //inout d1_n
,   .d1_p           ( MIPI_HARD_D1_P    ) //inout d1_p
,   .d2_n           (                   ) //inout d2_n
,   .d2_p           (                   ) //inout d2_p
,   .d3_n           (                   ) //inout d3_n
,   .d3_p           (                   ) //inout d3_p

,   .rx_clk_o       ( byte_clk          ) //output rx_clk_o

,   .d0ln_hsrxd     ( d0ln_hsrxd        ) //output [7:0] d0ln_hsrxd
,   .d1ln_hsrxd     ( d1ln_hsrxd        ) //output [7:0] d1ln_hsrxd
,   .d0ln_hsrxd_vld ( hsrxd_vld[0]      ) //output d0ln_hsrxd_vld
,   .d1ln_hsrxd_vld ( hsrxd_vld[1]      ) //output d1ln_hsrxd_vld

,   .hsrx_en_ck     ( 1'b1              ) //input hsrx_en_ck
,   .hsrx_en_d0     ( 1'b1              ) //input hsrx_en_d0
,   .hsrx_en_d1     ( 1'b1              ) //input hsrx_en_d1
,   .hsrx_odten_ck  ( 1'b1              ) //input hsrx_odten_ck
,   .hsrx_odten_d0  ( odt_en[0]         ) //input hsrx_odten_d0
,   .hsrx_odten_d1  ( odt_en[1]         ) //input hsrx_odten_d1
    
,   .lprx_en_ck     ( 1'b1              ) //input lprx_en_ck
,   .lprx_en_d0     ( 1'b1              ) //input lprx_en_d0
,   .lprx_en_d1     ( 1'b1              ) //input lprx_en_d1
,   .di_lprx0_n     ( lp_data0[0]       ) //output di_lprx0_n
,   .di_lprx0_p     ( lp_data0[1]       ) //output di_lprx0_p
,   .di_lprx1_n     ( lp_data1[0]       ) //output di_lprx1_n
,   .di_lprx1_p     ( lp_data1[1]       ) //output di_lprx1_p
,   .di_lprxck_n    (                   ) //output di_lprxck_n
,   .di_lprxck_p    (                   ) //output di_lprxck_p

,   .lptxen_ln0     ( 1'b0              ) //input lptxen_ln0
,   .lptxen_ln1     ( 1'b0              ) //input lptxen_ln1
,   .lptxen_lnck    ( 1'b0              ) //input lptxen_lnck

,   .do_lptx0_n     ( 1'b1              ) //input do_lptx0_n
,   .do_lptx1_n     ( 1'b1              ) //input do_lptx1_n
,   .do_lptxck_n    ( 1'b1              ) //input do_lptxck_n
,   .do_lptx0_p     ( 1'b1              ) //input do_lptx0_p
,   .do_lptx1_p     ( 1'b1              ) //input do_lptx1_p
,   .do_lptxck_p    ( 1'b1              ) //input do_lptxck_p

);

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 - 2'd1;
end

always @(posedge byte_clk) 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 <= (hsrx_cnt>0);
    byte_ready  <= hsrx_en_msk & hsrxd_vld[0];
    byte_d0     <= d0ln_hsrxd[7:0];
    byte_d1     <= d1ln_hsrxd[7:0];
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_RGB888         ) //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 [1:0] O_PAYLOAD_DV
,   .O_PAYLOAD      ( payload           ) //output [15: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


Byte_to_Pixel_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 [1:0] I_PAYLOAD_DV
,   .I_PAYLOAD      ( payload           ) //input [15: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 [7: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
