///////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------
//
// © 2021 Microchip Technology Inc. and its subsidiaries
// All rights reserved.
//
// ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN 
// ACCORDANCE WITH THE MICROCHIP LICENSE AGREEMENT AND MUST BE APPROVED 
// IN ADVANCE IN WRITING.

//  File Name       : prbs_asic_chk.v 
//
//  Description     : This module implements the prbs checker logic.

//  Targeted device : Family           Die          Package
//                    SmartFusion2     M2S090TS     484 FBGA
//  Author          : Solutions Team
//                              

//-------------------------------------------------------------------------

`define PRBS_ERR_CYC_CNT
module prbs_asic_chk (
  input             scan_mode,      
  input             arxclk,      
  input             arxrstn,
  input             atxclk,      
  input             cdrpll_fine,
  input             meso_lpbk,
  output            meso_sync,      
  input             custom_sel,
  input             custom_auto,
  input             custom_skip,
  input  [79:0]     custom_pattern,
  output            custom_sync,
  input             custom_chk,
  output [3:0]      custom_err,
  output [2:0]      custom_state,
  output            rxskip,
  input             prbs_en,
  input  [1:0]      prbs_typ,
  input  [4:0]      prbs_rxn,
  input  [19:0]     prbs_in,
  input             reg_clk,      
  input             reg_rstn,      
  output            prbs_cur,
`ifdef PRBS_ERR_CYC_CNT
  output reg [49:0] prbs_err_cyc_first,
  output reg [49:0] prbs_err_cyc_last,
`endif      
  output reg [7:0]  prbs_err,
  output	    det_err);
  
  reg prbs_en_meta;
  reg prbs_en_sync;
  reg prbs_en_sync_r;
  reg [1:0] prbs_typ_mem;
  reg cdrpll_fine_meta;
  reg cdrpll_fine_sync;
  
  always @(posedge arxclk or negedge arxrstn)
  begin
    if (!arxrstn) 
      begin 
        prbs_en_meta <= 1'b0;
        prbs_en_sync <= 1'b0;
        prbs_en_sync_r <= 1'b0;
        prbs_typ_mem <= 2'b00;
        cdrpll_fine_meta <= 1'b0;
        cdrpll_fine_sync <= 1'b0;
      end
    else 
      begin
        prbs_en_meta <= prbs_en;
        prbs_en_sync <= prbs_en_meta;
        prbs_en_sync_r <= prbs_en_sync;
        if (prbs_en_sync && !prbs_en_sync_r) prbs_typ_mem <= prbs_typ;
        cdrpll_fine_meta <= cdrpll_fine;
        cdrpll_fine_sync <= cdrpll_fine_meta;
      end
  end
  assign prbs_cur = prbs_en_sync;
  reg [30:0] prbs31;
  
  reg [30:0] prbs_7_5;
  reg [30:0] prbs_7_10;
  reg [30:0] prbs_7_20;
  reg [30:0] prbs_7_8;
  reg [30:0] prbs_7_16;
  always @*
  begin : next_prbs_7
    integer i,j,k,l,m; 
    prbs_7_5 = prbs31;
    prbs_7_10 = prbs31;
    prbs_7_20 = prbs31;
    prbs_7_8  = prbs31;
    prbs_7_16 = prbs31;
    for (i=0;i<5;i=i+1) begin : loop_prbs7_5
      prbs_7_5 = {prbs_7_5[29:0],prbs_7_5[6]^prbs_7_5[5]};
    end
    for (j=0;j<10;j=j+1) begin : loop_prbs7_10
      prbs_7_10 = {prbs_7_10[29:0],prbs_7_10[6]^prbs_7_10[5]};
    end
    for (k=0;k<20;k=k+1) begin : loop_prbs7_20
      prbs_7_20 = {prbs_7_20[29:0],prbs_7_20[6]^prbs_7_20[5]};
    end
    for (l=0;l<8;l=l+1) begin : loop_prbs7_8
      prbs_7_8 = {prbs_7_8[29:0],prbs_7_8[6]^prbs_7_8[5]};
    end
    for (m=0;m<16;m=m+1) begin : loop_prbs7_16
      prbs_7_16 = {prbs_7_16[29:0],prbs_7_16[6]^prbs_7_16[5]};
    end
  end
  
  reg [30:0] prbs_11_5;
  reg [30:0] prbs_11_10;
  reg [30:0] prbs_11_20;
  reg [30:0] prbs_11_8;
  reg [30:0] prbs_11_16;
  always @*
  begin : next_prbs_11
    integer i,j,k,l,m; 
    prbs_11_5  = prbs31;
    prbs_11_10 = prbs31;
    prbs_11_20 = prbs31;
    prbs_11_8  = prbs31;
    prbs_11_16 = prbs31;
    for (i=0;i<5;i=i+1) begin : loop_prbs11_5
      prbs_11_5 = {prbs_11_5[29:0],prbs_11_5[10]^prbs_11_5[8]};
    end
    for (j=0;j<10;j=j+1) begin : loop_prbs11_10
      prbs_11_10 = {prbs_11_10[29:0],prbs_11_10[10]^prbs_11_10[8]};
    end
    for (k=0;k<20;k=k+1) begin : loop_prbs11_20
      prbs_11_20 = {prbs_11_20[29:0],prbs_11_20[10]^prbs_11_20[8]};
    end
    for (l=0;l<8;l=l+1) begin : loop_prbs11_8
      prbs_11_8 = {prbs_11_8[29:0],prbs_11_8[10]^prbs_11_8[8]};
    end
    for (m=0;m<16;m=m+1) begin : loop_prbs11_16
      prbs_11_16 = {prbs_11_16[29:0],prbs_11_16[10]^prbs_11_16[8]};
    end
  end
  
  reg [30:0] prbs_23_5;
  reg [30:0] prbs_23_10;
  reg [30:0] prbs_23_20;
  reg [30:0] prbs_23_8;
  reg [30:0] prbs_23_16;
  always @*
  begin : next_prbs_23
    integer i,j,k,l,m; 
    prbs_23_5  = prbs31;
    prbs_23_10 = prbs31;
    prbs_23_20 = prbs31;
    prbs_23_8  = prbs31;
    prbs_23_16 = prbs31;
    for (i=0;i<5;i=i+1) begin : loop_prbs23_5
      prbs_23_5 = {prbs_23_5[29:0],prbs_23_5[22]^prbs_23_5[17]};
    end
    for (j=0;j<10;j=j+1) begin : loop_prbs23_10
      prbs_23_10 = {prbs_23_10[29:0],prbs_23_10[22]^prbs_23_10[17]};
    end
    for (k=0;k<20;k=k+1) begin : loop_prbs23_20
      prbs_23_20 = {prbs_23_20[29:0],prbs_23_20[22]^prbs_23_20[17]};
    end
    for (l=0;l<8;l=l+1) begin : loop_prbs23_8
      prbs_23_8 = {prbs_23_8[29:0],prbs_23_8[22]^prbs_23_8[17]};
    end
    for (m=0;m<16;m=m+1) begin : loop_prbs23_16
      prbs_23_16 = {prbs_23_16[29:0],prbs_23_16[22]^prbs_23_16[17]};
    end
  end
  
  reg [30:0] prbs_31_5;
  reg [30:0] prbs_31_10;
  reg [30:0] prbs_31_20;
  reg [30:0] prbs_31_8;
  reg [30:0] prbs_31_16;
  always @*
  begin : next_prbs_31
    integer i,j,k,l,m; 
    prbs_31_5  = prbs31;
    prbs_31_10 = prbs31;
    prbs_31_20 = prbs31;
    prbs_31_8  = prbs31;
    prbs_31_16 = prbs31;
    for (i=0;i<5;i=i+1) begin : loop_prbs31_5
      prbs_31_5 = {prbs_31_5[29:0],prbs_31_5[30]^prbs_31_5[27]};
    end
    for (j=0;j<10;j=j+1) begin : loop_prbs31_10
      prbs_31_10 = {prbs_31_10[29:0],prbs_31_10[30]^prbs_31_10[27]};
    end
    for (k=0;k<20;k=k+1) begin : loop_prbs31_20
      prbs_31_20 = {prbs_31_20[29:0],prbs_31_20[30]^prbs_31_20[27]};
    end
    for (l=0;l<8;l=l+1) begin : loop_prbs31_8
      prbs_31_8 = {prbs_31_8[29:0],prbs_31_8[30]^prbs_31_8[27]};
    end
    for (m=0;m<16;m=m+1) begin : loop_prbs31_16
      prbs_31_16 = {prbs_31_16[29:0],prbs_31_16[30]^prbs_31_16[27]};
    end
  end

  reg [7:0] prbs_cnt;
  reg prbs_chk;
  
  reg [19:0] prbs_datain;
  always @*
  begin : loop_inv
    integer i;
    for (i=0; i<20;i=i+1) begin
      prbs_datain[i] = ~prbs_in[19-i];
    end
  end
  reg [19:0] prbs_datain_r;
  reg eq_4_0;  
  reg eq_9_5;  
  reg eq_19_10; 
  reg eq_7_0; 
  reg eq_15_8; 
  reg [7:0] prbs_err_cnt;
  reg [7:0] prbs_err_mem;
  reg err_lvl, err_ack_meta, err_ack_sync;
  reg prbs_inv;
  always @(posedge arxclk or negedge arxrstn)
  begin
    if (!arxrstn) 
      begin 
        prbs_cnt <= 8'h00;
        prbs_chk <= 1'b0;
        prbs31 <= {31{1'b1}};
        prbs_err_cnt <= 8'h00;
        prbs_datain_r <= 20'h00000;
        prbs_inv <= 1'b0;
      end
    else if (!prbs_en_sync_r) 
      begin
        prbs_err_cnt <= 8'hFF;
        prbs_cnt <= 8'h00;
        prbs_chk <= 1'b0;
        prbs31 <= {31{1'b1}};
        prbs_inv <= 1'b0;
      end
    else 
      begin
        if (!cdrpll_fine_sync) prbs_cnt <= 8'h00;
        else if (prbs_cnt!=8'hFF) prbs_cnt <= prbs_cnt + 1'b1;
        prbs_chk <= (prbs_cnt==8'hFF);
        prbs_inv <= (prbs31==31'h0);
        if (prbs_chk) begin
          case (prbs_typ_mem)
            2'b00 : begin  // PRBS 7
              if      (prbs_rxn==5'd4)  prbs31 <= prbs_7_5;
              else if (prbs_rxn==5'd7)  prbs31 <= prbs_7_8;
              else if (prbs_rxn==5'd9)  prbs31 <= prbs_7_10;
              else if (prbs_rxn==5'd15) prbs31 <= prbs_7_16;
              else                      prbs31 <= prbs_7_20;
            end
            2'b01 : begin  // PRBS11
              if      (prbs_rxn==5'd4)  prbs31 <= prbs_11_5;
              else if (prbs_rxn==5'd7)  prbs31 <= prbs_11_8;
              else if (prbs_rxn==5'd9)  prbs31 <= prbs_11_10;
              else if (prbs_rxn==5'd15) prbs31 <= prbs_11_16;
              else                      prbs31 <= prbs_11_20;
            end
            2'b10 : begin  // PRBS23
              if      (prbs_rxn==5'd4)  prbs31 <= prbs_23_5;
              else if (prbs_rxn==5'd7)  prbs31 <= prbs_23_8;
              else if (prbs_rxn==5'd9)  prbs31 <= prbs_23_10;
              else if (prbs_rxn==5'd15) prbs31 <= prbs_23_16;
              else                      prbs31 <= prbs_23_20;
            end
            2'b11 : begin  // PRBS31
              if      (prbs_rxn==5'd4)  prbs31 <= prbs_31_5;
              else if (prbs_rxn==5'd7)  prbs31 <= prbs_31_8;
              else if (prbs_rxn==5'd9)  prbs31 <= prbs_31_10;
              else if (prbs_rxn==5'd15) prbs31 <= prbs_31_16;
              else                      prbs31 <= prbs_31_20;
            end
            default : begin
              prbs31 <= {31{1'b1}};
            end
          endcase
        end else begin          
          if      (prbs_rxn==5'd4)  prbs31 <= {prbs31[25:0],prbs_datain[4:0]};
          else if (prbs_rxn==5'd7)  prbs31 <= {prbs31[22:0],prbs_datain[7:0]};
          else if (prbs_rxn==5'd9)  prbs31 <= {prbs31[20:0],prbs_datain[9:0]};
          else if (prbs_rxn==5'd15) prbs31 <= {prbs31[14:0],prbs_datain[15:0]};
          else                      prbs31 <= {prbs31[10:0],prbs_datain[19:0]};
        end
        prbs_datain_r <= prbs_datain[19:0];
        if (!prbs_chk && prbs_cnt==8'hFF) prbs_err_cnt <= 8'h00;
        else if (prbs_chk && det_err && prbs_err_cnt<8'hFE) prbs_err_cnt <= prbs_err_cnt + 1'b1;      
      end
  end

  always @(posedge arxclk or negedge arxrstn)
  begin
    if (!arxrstn) 
      begin 
         eq_4_0 <= 1'b0;
         eq_9_5 <= 1'b0;
         eq_19_10 <= 1'b0;
         eq_7_0 <= 1'b0;
         eq_15_8 <= 1'b0;
      end
    else
      begin
         eq_4_0 <= (prbs_datain_r[4:0]==prbs31[4:0]);  
         eq_9_5 <= (prbs_datain_r[9:5]==prbs31[9:5]);  
         eq_19_10 <= (prbs_datain_r[19:10]==prbs31[19:10]); 
         eq_7_0 <= (prbs_datain_r[7:0]==prbs31[7:0]);  
         eq_15_8 <= (prbs_datain_r[15:8]==prbs31[15:8]);  
      end
 end
  assign det_err = (prbs_inv       )? 1'b1:
                   (prbs_rxn==5'd4 )? ~eq_4_0 :
                   (prbs_rxn==5'd7 )? ~eq_7_0 : 
                   (prbs_rxn==5'd9 )? ~(eq_4_0 & eq_9_5) : 
                   (prbs_rxn==5'd15)? ~(eq_7_0 & eq_15_8) : 
                                      ~(eq_4_0 & eq_9_5 & eq_19_10);
                                       
  reg err_lvl_sync_r; 
                                    
  always @(posedge arxclk or negedge arxrstn)
  begin
    if (!arxrstn) 
      begin 
        err_ack_meta <= 1'b0;
        err_ack_sync <= 1'b0;
        err_lvl <= 1'b0;
        prbs_err_mem <= 8'h00;
      end
    else 
      begin
        err_ack_meta <= err_lvl_sync_r;
        err_ack_sync <= err_ack_meta;
        if (prbs_err_cnt!=prbs_err_mem && err_lvl==err_ack_sync) begin
          err_lvl <= ~err_lvl;
          prbs_err_mem <= prbs_err_cnt;
        end        
      end
  end
  
  reg err_lvl_meta;
  reg err_lvl_sync;
  always @(posedge reg_clk or negedge reg_rstn)
  begin
    if (!reg_rstn) 
      begin 
        err_lvl_meta <= 1'b0;
        err_lvl_sync <= 1'b0;
        err_lvl_sync_r <= 1'b0;
        prbs_err <= 8'h00;
      end
    else 
      begin
        err_lvl_meta <= err_lvl;
        err_lvl_sync <= err_lvl_meta;
        err_lvl_sync_r <= err_lvl_sync;
        if (err_lvl_sync_r!=err_lvl_sync) prbs_err <= prbs_err_mem;
      end
  end


`ifdef PRBS_ERR_CYC_CNT
  // Implement 50 bit counter for reporting first PRBS error detected and last PRBS error detected
  reg [49:0] prbs_err_cyc, prbs_err_cyc_capt, prbs_err_cyc_mem;
  reg prbs_err_sat;
  wire [49:0] all_ones = {50{1'b1}};
  always @(posedge arxclk or negedge arxrstn)
  begin
    if (!arxrstn) 
      begin 
        prbs_err_cyc <= 50'b0;
        prbs_err_cyc_capt <= 50'b0;
        prbs_err_cyc_mem <= 50'b0;
        prbs_err_sat <= 1'b1;
      end 
    else
      begin
        if (!prbs_chk && prbs_cnt==8'hFF) prbs_err_cyc <= 50'b0;
        else if (!prbs_err_sat) prbs_err_cyc <= prbs_err_cyc + 1'b1;
        
        if (prbs_chk && prbs_err_cyc[49:1]==all_ones[49:1]) prbs_err_sat <= 1'b1;
        else prbs_err_sat <= 1'b0;
        
        if (!prbs_chk && prbs_cnt==8'hFF) prbs_err_cyc_capt <= 50'b0;
        else if (prbs_chk && det_err && prbs_err_cnt<8'hFE) prbs_err_cyc_capt <= prbs_err_cyc; 
             
        if (prbs_err_cnt!=prbs_err_mem && err_lvl==err_ack_sync) begin
          prbs_err_cyc_mem <= prbs_err_cyc_capt;
        end        
        
      end
  end  
  
  always @(posedge reg_clk or negedge reg_rstn)
  begin
    if (!reg_rstn) 
      begin 
        prbs_err_cyc_first <= 50'b0;
        prbs_err_cyc_last <= 50'b0;
      end
    else 
      begin
        if (err_lvl_sync_r!=err_lvl_sync && prbs_err_mem==8'h01) prbs_err_cyc_first <= prbs_err_cyc_mem;
        if (err_lvl_sync_r!=err_lvl_sync) prbs_err_cyc_last <= prbs_err_cyc_mem;
      end
  end
`endif //PRBS_ERR_CYC_CNT
  
  
`ifdef CUSTOM_PATTERN_SUPPORT

  reg custom_sel_meta;
  reg custom_sel_sync;  
  reg custom_chk_meta;
  reg custom_chk_sync;  
  reg custom_auto_meta;
  reg custom_auto_sync;  
  always @(posedge arxclk or negedge arxrstn)
  begin
    if (!arxrstn) 
      begin 
        custom_sel_meta <= 1'b0;
        custom_sel_sync <= 1'b0;
        custom_chk_meta <= 1'b0;
        custom_chk_sync <= 1'b0;
        custom_auto_meta <= 1'b0;
        custom_auto_sync <= 1'b0;
      end
    else 
      begin
        custom_sel_meta <= custom_sel;
        custom_sel_sync <= custom_sel_meta;
        custom_chk_meta <= custom_chk;
        custom_chk_sync <= custom_chk_meta;
        custom_auto_meta <= custom_auto;
        custom_auto_sync <= custom_auto_meta;
      end
  end
   
  // custom pattern checker
  reg [4:0] custom_cnt;
  reg [4:0] custom_max;
  reg [79:0] mem_data;
  reg custom_match;
  wire custom_count_one;
  always @(posedge arxclk or negedge arxrstn)
  begin
    if (!arxrstn) 
      begin 
        custom_cnt <= 5'd0;
        custom_max <= 5'd19;
        mem_data <= 80'h0;
        custom_match <= 1'b0;
      end
    else 
      begin
        if (!custom_sel_sync || !cdrpll_fine_sync || custom_cnt==custom_max) custom_cnt <= 5'h0;
        else if (custom_count_one) custom_cnt <= 5'h1;
        else custom_cnt <= custom_cnt + 1'b1;
        custom_max <= (prbs_rxn==5'd4 )? 5'd15 : //16*5=80
                      (prbs_rxn==5'd7 )? 5'd9 :  //10*8=80
                      (prbs_rxn==5'd9 )? 5'd7 :  //8*10=80
                      (prbs_rxn==5'd15)? 5'd4 :  //5*16=80
                                         5'd3 ;  //4*20=80
        if (!custom_sel_sync) mem_data <= 80'h0;
        else if (prbs_rxn==5'd4)  mem_data <= { prbs_in[4:0] ,mem_data[79:5]};  //{mem_data[74:0],prbs_in[4:0]};  
        else if (prbs_rxn==5'd7)  mem_data <= { prbs_in[7:0] ,mem_data[79:8]};  //{mem_data[71:0],prbs_in[7:0]};  
        else if (prbs_rxn==5'd9)  mem_data <= { prbs_in[9:0] ,mem_data[79:10]};  //{mem_data[69:0],prbs_in[9:0]};  
        else if (prbs_rxn==5'd15) mem_data <= { prbs_in[15:0],mem_data[79:16]}; //{mem_data[63:0],prbs_in[15:0]}; 
        else                      mem_data <= { prbs_in[19:0],mem_data[79:20]}; //{mem_data[59:0],prbs_in[19:0]}; 
        custom_match <= (mem_data==custom_pattern); 
      end
  end
  
  
  localparam [2:0] custom_idle     = 3'b000;
  localparam [2:0] custom_wait     = 3'b001;
  localparam [2:0] custom_sync_chk = 3'b010;
  localparam [2:0] custom_skipbit  = 3'b011;
  localparam [2:0] custom_sync_val = 3'b100;
  localparam [2:0] custom_synced   = 3'b101;
  
  reg [2:0] custom_sm;
  reg [7:0] clk_cnt;
  reg [3:0] errcnt;
  reg rxskipbit;
  always @(posedge arxclk or negedge arxrstn)
  begin
    if (!arxrstn) 
      begin 
        custom_sm <= custom_idle;
        rxskipbit <= 1'b0;
        clk_cnt <= 8'h0;
        errcnt <= 4'h0;
      end
    else 
      begin
        case (custom_sm)
          custom_idle : begin
           rxskipbit <= 1'b0;
           clk_cnt <= 8'h0;
           if (custom_sel_sync && custom_auto_sync && cdrpll_fine_sync) custom_sm <= custom_wait;
           else if (custom_chk_sync && !custom_auto_sync && cdrpll_fine_sync) custom_sm <= custom_sync_chk;
          end
          custom_wait : begin
            rxskipbit <= 1'b0;
            clk_cnt <= clk_cnt + 1'b1;
            if (clk_cnt==8'hFF) custom_sm <= custom_sync_chk;
          end
          custom_sync_chk :  begin
            rxskipbit <= 1'b0;
            if (clk_cnt[5:1]>=custom_max || custom_match) clk_cnt <= 8'h0;
            else clk_cnt <= clk_cnt + 1'b1;
            if (custom_match) custom_sm <= custom_sync_val;
            else if (clk_cnt[5:1]>=custom_max) custom_sm <= custom_skipbit;
          end
          custom_skipbit :  begin
            custom_sm <= custom_sync_chk;
            rxskipbit <= 1'b1;
          end
          custom_sync_val :  begin
            if (clk_cnt[5:1]>=custom_max || custom_match) clk_cnt <= 8'h0;
            else clk_cnt <= clk_cnt + 1'b1;
            if (!custom_sel_sync) custom_sm <= custom_idle;
            else if (custom_match && custom_cnt==5'h0) custom_sm <= custom_synced;
            else if (clk_cnt[5:1]>=custom_max) custom_sm <= custom_sync_chk;
          end
          custom_synced :  begin
            if (!custom_sel_sync) custom_sm <= custom_idle;
          end
          default: begin
            custom_sm <= custom_idle;
          end
        endcase
        if (!custom_chk_sync) errcnt <= 4'h0;
        else if (custom_sm==custom_synced && custom_cnt==5'h0 && errcnt!=4'hF && !custom_match) errcnt <= errcnt + 1'b1;
      end
  end
  
  assign custom_count_one = (custom_sm==custom_sync_chk) && custom_match;
  
  reg meso_skip;
  assign rxskip = rxskipbit | custom_skip | meso_skip;
  
  reg errcnt_ack_meta, errcnt_ack_sync, errcnt_lvl;
  reg errcnt_lvl_sync_r;
  reg [3:0] errcnt_mem;
  always @(posedge arxclk or negedge arxrstn)
  begin
    if (!arxrstn) 
      begin 
        errcnt_ack_meta <= 1'b0;
        errcnt_ack_sync <= 1'b0;
        errcnt_lvl <= 1'b0;
        errcnt_mem <= 4'h0;
      end
    else 
      begin
        errcnt_ack_meta <= errcnt_lvl_sync_r;
        errcnt_ack_sync <= errcnt_ack_meta;
        if (errcnt!=errcnt_mem && errcnt_lvl==errcnt_ack_sync) begin
          errcnt_lvl <= ~errcnt_lvl;
          errcnt_mem <= errcnt;
        end        
      end
  end
  
  reg errcnt_lvl_meta;
  reg errcnt_lvl_sync;
  reg [3:0] errcnt_regclk;
  always @(posedge reg_clk or negedge reg_rstn)
  begin
    if (!reg_rstn) 
      begin 
        errcnt_lvl_meta <= 1'b0;
        errcnt_lvl_sync <= 1'b0;
        errcnt_lvl_sync_r <= 1'b0;
        errcnt_regclk <= 4'h0;
      end
    else 
      begin
        errcnt_lvl_meta <= errcnt_lvl;
        errcnt_lvl_sync <= errcnt_lvl_meta;
        errcnt_lvl_sync_r <= errcnt_lvl_sync;
        if (errcnt_lvl_sync_r!=errcnt_lvl_sync) errcnt_regclk <= errcnt_mem;
      end
  end

  // 2 flop for being metastable clean without guarantying the read value (ok for debug) 
  reg [2:0] custom_sm_meta, custom_sm_sync;
  always @(posedge reg_clk or negedge reg_rstn)
  begin
    if (!reg_rstn) 
      begin 
        custom_sm_meta <= 3'b0;
        custom_sm_sync <= 3'h0;
      end
    else 
      begin
        custom_sm_meta <= custom_sm;
        custom_sm_sync <= custom_sm_meta;
      end
  end
  
  assign custom_sync = (custom_sm_sync==custom_synced);
  assign custom_err = errcnt_regclk;
  assign custom_state = custom_sm_sync;
  
`else  
  reg meso_skip;
  assign rxskip = custom_skip | meso_skip;
  assign custom_err = 4'h0;
  assign custom_state = 3'h0;
  assign custom_sync = 1'b0;
`endif

  // mesochronous loopback logic
  // capture txclk on rxclk to detect when rxclk and txclk are in phase
  reg meso_txclk_meta,meso_txclk_sync;
  wire atxclk_scan_disab = atxclk & ~scan_mode;
  always @(posedge arxclk or negedge arxrstn)
  begin
    if (!arxrstn) 
      begin 
        meso_txclk_meta <= 1'b0;
        meso_txclk_sync <= 1'b0;
      end
    else 
      begin 
        meso_txclk_meta <= atxclk_scan_disab;
        meso_txclk_sync <= meso_txclk_meta;
      end
  end

  reg meso_lpbk_meta,meso_lpbk_sync;
  always @(posedge arxclk or negedge arxrstn)
  begin
    if (!arxrstn) 
      begin 
        meso_lpbk_meta <= 1'b0;
        meso_lpbk_sync <= 1'b0;
      end
    else 
      begin 
        meso_lpbk_meta <= meso_lpbk;
        meso_lpbk_sync <= meso_lpbk_meta;
      end
  end

  localparam [2:0] meso_idle   = 3'b000;
  localparam [2:0] meso_check0 = 3'b001;
  localparam [2:0] meso_skp0   = 3'b010;
  localparam [2:0] meso_check1 = 3'b011;
  localparam [2:0] meso_skp1   = 3'b100;
  localparam [2:0] meso_synced = 3'b101;
  reg [2:0] meso_sm;
  reg [3:0] meso_cnt;  
  always @(posedge arxclk or negedge arxrstn)
  begin
    if (!arxrstn) 
      begin 
        meso_sm <= meso_idle;
        meso_skip <= 1'b0;
        meso_cnt <= 4'b0000;
      end
    else 
      begin
        case (meso_sm)
          meso_idle : begin
            meso_skip <= 1'b0;
            meso_cnt <= 4'b0000;
            if (meso_lpbk_sync && cdrpll_fine_sync) meso_sm <= meso_check0;
          end
          meso_check0 : begin
            meso_skip <= 1'b0;
            meso_cnt <= meso_cnt + 1'b1;
            if (meso_cnt==4'b1111 && !meso_txclk_sync)  meso_sm <= meso_skp1;
            else if (meso_cnt==4'b1111) meso_sm <= meso_skp0;
          end
          meso_skp0 :  begin
            meso_skip <= 1'b1;
            meso_cnt <= 4'b0000;
            meso_sm <= meso_check0;
          end
          meso_skp1 :  begin
            meso_skip <= 1'b1;
            meso_cnt <= 4'b0000;
            meso_sm <= meso_check1;
          end
          meso_check1 : begin
            meso_skip <= 1'b0;
            meso_cnt <= meso_cnt + 1'b1;
            if (meso_cnt==4'b1111 && meso_txclk_sync)  meso_sm <= meso_synced;
            else if (meso_cnt==4'b1111) meso_sm <= meso_skp1;
          end
          meso_synced : begin
            meso_skip <= 1'b0;
            meso_cnt <= 4'b0000;
            if (!meso_lpbk_sync) meso_sm <= meso_idle;
          end
          default: begin
            meso_sm <= meso_idle;
          end
        endcase
      end
  end
  
  
  reg [2:0] meso_sm_meta, meso_sm_sync;
  always @(posedge reg_clk or negedge reg_rstn)
  begin
    if (!reg_rstn) 
      begin 
        meso_sm_meta <= 3'b0;
        meso_sm_sync <= 3'h0;
      end
    else 
      begin
        meso_sm_meta <= meso_sm;
        meso_sm_sync <= meso_sm_meta;
      end
  end
  
  assign meso_sync = (meso_sm_sync==meso_synced);
                                   
endmodule