`define     HS_EN_DLY           8
`define     HS_RST_HOLD_DLY     4

module MIPI_CSI_HardPhy_Rx
(   input           RESET_N
,   input           CAM_INIT_DONE
//,   input           DRP_CLK
//! 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_BYTE_CLK
,   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

,   output          O_BYTE_READY

,   output          O_RX_DRST_N
,   output [1:0]    O_LP0
,   output [7:0]    O_BYTE_D0
);

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            reset_with_lock_n;
wire            pll_lock;
//
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  [15:0] wc_r        = 'b0;
reg  [ 5:0] dt_r        = 'b0;
reg         sp_en_r     = 'b0;
reg         lp_av_en_r  = 'b0;
//
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 */;
//

// 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 & CAM_INIT_DONE)) //input reset
,   .clkin          ( byte_clk          ) //input clkin
,   .lock           ( pll_lock          ) //output lock
,   .clkout0        ( lvds_pclk         ) //output clkout0 - 4/5ths of input clock
,   .clkout1        (O_DOUBLE_PIXEL_CLK ) //output clkout1 - double clkout0
,   .mdclk          (byte_clk)
);

assign reset_with_lock_n = RESET_N & pll_lock;

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           (                   ) //inout d1_n
,   .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     ( reset_with_lock_n ) //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_with_lock_n) begin
    if (~reset_with_lock_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_with_lock_n)           reg3to1     <= 'b0;
    else if (~reg3to1)      reg3to1     <= from3to1;
    else if (1)             reg3to1     <= ~from1toX;
//!______________________________________________________________________________
    if (~reset_with_lock_n)           hsrx_cnt    <= 'b0;
    else if (odt_en)        hsrx_cnt    <= `HS_EN_DLY;
    else if (hsrx_cnt>0)    hsrx_cnt    <= hsrx_cnt - 'd1;
end

reg [3:0] rx_drst_count = 4'd0;

always @(posedge byte_clk) begin
    lp0_reg_0   <= lp_data0;
    // lp1_reg_0   <= lp_data1;
    lp0_reg_1   <= lp0_reg_0;
//!______________________________________________________________________________
    // Hold reset for 4 cycles starting with the LP11-01-00 transition
    if(reg3to1&from1to0) begin
        rx_drst_n   <= 1'b0;
        rx_drst_count <= (4'd`HS_RST_HOLD_DLY - 4'd1);
    end
    else if(rx_drst_count != 4'd0) begin
        rx_drst_count <= rx_drst_count - 4'd1;
    end 
    else begin
        rx_drst_n <= 1'b1;
    end
    // 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_with_lock_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;
//!______________________________________________________________________________
    dt_r            <= dt;
    wc_r            <= wc;
end


MIPI_Byte_to_Pixel_Converter_Top u_b2p
(   .I_RSTN         ( reset_with_lock_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_r              ) //input [5:0] I_DT
,   .I_WC           ( wc_r              ) //input [15:0] I_WC
,   .I_PAYLOAD_DV   ( payload_dv_r      ) //input I_PAYLOAD_DV
,   .I_PAYLOAD      ( payload_r         ) //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;

assign O_BYTE_CLK = byte_clk;

always @(posedge byte_clk or negedge reset_with_lock_n) begin
    if(!reset_with_lock_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 bytecnt_200k = (total_av_byte_count == 18'd200_000); // 500 bytes per line, 400 lines

assign O_FV_BYTE_CLK = fv_byte_clock;
assign O_BYTE_COUNT_200K = bytecnt_200k;
assign O_BYTE_READY = byte_ready;

assign O_RX_DRST_N = rx_drst_n;
assign O_LP0 = lp_data0;
assign O_BYTE_D0 = byte_d0;

endmodule
