`define     SOFT_HS_EN_DLY   4

module MIPI_CSI_SoftPhy_Rx
(   input           RESET_N
,   input           CAM_INIT_DONE
//,   input           DRP_CLK
//! 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
,   output          O_ODT_EN
,   output          O_BADPACKET
);

localparam  DT_RAW8 = 6'h2A;
localparam  DT_RAW10  = 6'h2B;

wire            byte_clk    /* synthesis syn_keep = 1 */;
wire            lvds_pclk_s   /* 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            rx_drst_n   = ~((lp0_reg_0==1)&&(lp_data0==0));
//
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);

reg badpacket /* synthesis syn_keep = 1 */;

assign bytecnt_200k = (total_av_byte_count == 18'd200_000); // 500 bytes per line, 400 lines

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 O_FV_BYTE_CLK = fv_byte_clock;
assign O_BYTE_COUNT_200K = bytecnt_200k;

always @(posedge byte_clk or negedge reset_with_lock_n) begin
    if(!reset_with_lock_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)
            if(valid_hs_start != 8'h00)
                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           ( pll_lock          ) //output lock
,   .clkout0        ( lvds_pclk_s         ) //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;

wire SOFT_HS_CLK;
wire SOFT_HS_CLK_PRE_CE;
wire SOFT_HS_DATA;

MIPI_IBUF MIPI_IBUF_CLK  (
	.I(1'b0),
	.OEN(1'b1),
	.IO(MIPI_SOFT_CLK_P),
	.IB(1'b0),
	.IOB(MIPI_SOFT_CLK_N),
	.OENB(1'b1),
	.HSREN(1'b1),
	.HSEN(1'b1),
	.OB(),
	.OL(),
	.OH(SOFT_HS_CLK_PRE_CE )
);

DHCE MIPI_HS_CLK_ENABLE(
    .CLKIN(SOFT_HS_CLK_PRE_CE)
,   .CLKOUT(SOFT_HS_CLK)
,   .CEN(~CAM_INIT_DONE)
);

MIPI_IBUF MIPI_IBUF_LANE0  (
	.I(1'b0),
	.OEN(1'b1 ),
	.IO(MIPI_SOFT_D0_P),
	.IB(1'b0),
	.IOB(MIPI_SOFT_D0_N),
	.OENB(1'b1),
	.HSREN(odt_en[0]),
	.HSEN(odt_en[0]),
	.OB(lp_data0[0]),
	.OL(lp_data0[1]),
	.OH(SOFT_HS_DATA)
);

CLKDIV SOFT_CLKDIV  (
	.HCLKIN(SOFT_HS_CLK ),
	.RESETN(CAM_INIT_DONE),
	.CALIB(1'b0),
	.CLKOUT(byte_clk)
);
defparam SOFT_CLKDIV.DIV_MODE="4";

wire dataini_t0;

IODELAY SOFT_DELAY  (
	.DI(SOFT_HS_DATA),
	.SDTAP(1'b0),
	.VALUE(1'b0),
	.DLYSTEP({1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0}),
	.DO(dataini_t0),
	.DF( )
);
defparam SOFT_DELAY.C_STATIC_DLY=0;
defparam SOFT_DELAY.DYN_DLY_EN="FALSE";
defparam SOFT_DELAY.ADAPT_EN="FALSE";
IDES8 SOFT_IDES80  (
	.D(dataini_t0),
	.PCLK(byte_clk),
	.FCLK(SOFT_HS_CLK),
	.RESET(~reset_with_lock_n ),
	.CALIB(1'b0),
	.Q0(d0ln_hsrxd[0]),
	.Q1(d0ln_hsrxd[1]),
	.Q2(d0ln_hsrxd[2]),
	.Q3(d0ln_hsrxd[3]),
	.Q4(d0ln_hsrxd[4]),
	.Q5(d0ln_hsrxd[5]),
	.Q6(d0ln_hsrxd[6]),
	.Q7(d0ln_hsrxd[7])
);

reg [7:0] data_r0;
reg [7:0] data_r1;
reg [7:0] data_r2;
reg [7:0] data_r3;

always @(posedge byte_clk) begin
    data_r0 <= d0ln_hsrxd;
    data_r1 <= data_r0;
    data_r2 <= data_r1;
    data_r3 <= data_r2;
end

reg [7:0] valid_hs_start;
reg [23:0] temp_data_shift[7:0];

always @(*) begin
    integer i;
    for( i=0; i < 8; i=i+1) begin
        temp_data_shift[i] = ({data_r0, data_r1, data_r2, data_r3} >> i);
        valid_hs_start[i] = temp_data_shift[i] == 24'hB80000;
    end
end

reg [15:0] rx_drst_timer;
always @(posedge byte_clk or negedge reset_with_lock_n) begin
    if(!reset_with_lock_n) begin
        rx_drst_timer <= 16'd0;
    end
    else begin
        if(!rx_drst_n) begin
            // for the single cycle when drst is asserted, start the timer
            rx_drst_timer <= 16'd20; // generally we see the HS Start symbol (B8,00) within 16 clocks after reset
        end
        else begin
            if(rx_drst_timer > 'd0) begin
                rx_drst_timer <= rx_drst_timer - 'd1;
            end
        end
    end
end

wire rx_drst_timer_expired /* synthesis syn_keep = 1 */;
assign rx_drst_timer_expired = (rx_drst_timer == 'd0);

reg rx_sync;
reg [2:0] rx_sync_timer;
reg [2:0] rx_shift;

always @(posedge byte_clk or negedge rx_drst_n) begin
    if(!rx_drst_n) begin
        rx_sync <= 1'b0;
        rx_shift <= 3'h0;
        rx_sync_timer <= 3'd7;
    end 
    else begin
        if(rx_sync_timer > 3'd0) begin
            rx_sync_timer <= rx_sync_timer - 'd1;
        end else begin
            if(!rx_sync) begin
                if(valid_hs_start != 8'h00)
                    rx_sync <= 1'b1;
                case(valid_hs_start)
                8'b00000001: rx_shift <= 3'h0;
                8'b00000010: rx_shift <= 3'h1;
                8'b00000100: rx_shift <= 3'h2;
                8'b00001000: rx_shift <= 3'h3;
                8'b00010000: rx_shift <= 3'h4;
                8'b00100000: rx_shift <= 3'h5;
                8'b01000000: rx_shift <= 3'h6;
                8'b10000000: rx_shift <= 3'h7;
                endcase
            end
        end
    end
end

reg [7:0] shifted_byte_out;
always @(*) begin
    if(rx_sync) begin
        shifted_byte_out = {data_r2, data_r3} >> rx_shift;
    end
    else begin
        shifted_byte_out = 8'h00;
    end
end


//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_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_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    <= `SOFT_HS_EN_DLY;
    else if (hsrx_cnt>0)    hsrx_cnt    <= hsrx_cnt - 'd1;
//!______________________________________________________________________________
    if (~reset_with_lock_n)           hsrx_cnt_2    <= 'b0;
    else if (odt_en)        hsrx_cnt_2    <= 2*(`SOFT_HS_EN_DLY);
    else if (hsrx_cnt_2>0)    hsrx_cnt_2    <= hsrx_cnt_2 - 'd1;
end

always @(posedge byte_clk or negedge reset_with_lock_n) begin
    if(~reset_with_lock_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_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_READY        ( rx_sync & hsrx_en_msk  ) //input I_READY
,   .I_DATA0        ( shifted_byte_out           ) //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 or negedge reset_with_lock_n) begin
    if(!reset_with_lock_n) begin
        badpacket <= 1'b0;
    end 
    else begin
        if(ecc_ok && (sp_en || lp_en)) begin
            if(sp_en && ((dt == 6'd00) || (dt == 6'd01))) begin
                badpacket <= 1'b0;
            end 
            else if(lp_en && (dt == DT_RAW10) && (wc == 16'd500)) begin
                badpacket <= 1'b0;
            end else begin
                badpacket <= 1'b1;
            end
        end
        else if((!ecc_ok) && (sp_en || lp_en)) begin
            badpacket <= 1'b1;
        end
    end
end

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_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                ) //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_s         ) //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_s;
assign O_PIXEL_VS = csi_fv;
assign O_PIXEL_DE = csi_lv;
assign O_PIXEL_DATA = csi_pixel;

assign O_ODT_EN = odt_en[0];
assign O_BADPACKET = badpacket;

endmodule
