///////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------
//
// © 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_gen.v 
//
//  Description     : This module implements the prbs generator logic.

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

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

module prbs_asic_gen (
  input             atxclk,
  input             atxrstn,
  input             meso_lpbk,
  input [19:0]      arxdo,
  input [79:0]      custom_pattern,
  input             custom_sel,
  input             prbs_en,
  input  [1:0]      prbs_typ,
  input  [4:0]      prbs_txn,
  output            prbs_sel,
  output [19:0]     prbs_out);

  reg prbs_en_meta;
  reg prbs_en_sync;
  reg prbs_en_sync_r;
  reg [1:0] prbs_typ_mem;
  reg custom_sel_meta;
  reg custom_sel_sync;

  always @(posedge atxclk or negedge atxrstn)
  begin
    if (!atxrstn)
      begin
        prbs_en_meta <= 1'b0;
        prbs_en_sync <= 1'b0;
        prbs_en_sync_r <= 1'b0;
        prbs_typ_mem <= 2'b00;
        custom_sel_meta <= 1'b0;
        custom_sel_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;
        custom_sel_meta <= custom_sel;
        custom_sel_sync <= custom_sel_meta;
      end
  end
  assign prbs_sel = prbs_en_sync_r | custom_sel_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

  always @(posedge atxclk or negedge atxrstn)
  begin
    if (!atxrstn)
      begin
        prbs31 <= {31{1'b1}};
      end
    else if (!prbs_en_sync_r)
      begin
        prbs31 <= {31{1'b1}};
      end
    else
      begin
        case (prbs_typ_mem)
          2'b00 : begin  // PRBS 7
            if      (prbs_txn==5'd4)  prbs31 <= prbs_7_5;
            else if (prbs_txn==5'd7)  prbs31 <= prbs_7_8;
            else if (prbs_txn==5'd9)  prbs31 <= prbs_7_10;
            else if (prbs_txn==5'd15) prbs31 <= prbs_7_16;
            else                      prbs31 <= prbs_7_20;
          end
          2'b01 : begin  // PRBS11
            if      (prbs_txn==5'd4)  prbs31 <= prbs_11_5;
            else if (prbs_txn==5'd7)  prbs31 <= prbs_11_8;
            else if (prbs_txn==5'd9)  prbs31 <= prbs_11_10;
            else if (prbs_txn==5'd15) prbs31 <= prbs_11_16;
            else                      prbs31 <= prbs_11_20;
          end
          2'b10 : begin  // PRBS23
            if      (prbs_txn==5'd4)  prbs31 <= prbs_23_5;
            else if (prbs_txn==5'd7)  prbs31 <= prbs_23_8;
            else if (prbs_txn==5'd9)  prbs31 <= prbs_23_10;
            else if (prbs_txn==5'd15) prbs31 <= prbs_23_16;
            else                      prbs31 <= prbs_23_20;
          end
          2'b11 : begin  // PRBS31
            if      (prbs_txn==5'd4)  prbs31 <= prbs_31_5;
            else if (prbs_txn==5'd7)  prbs31 <= prbs_31_8;
            else if (prbs_txn==5'd9)  prbs31 <= prbs_31_10;
            else if (prbs_txn==5'd15) prbs31 <= prbs_31_16;
            else                      prbs31 <= prbs_31_20;
          end
          default : begin
            prbs31 <= {31{1'b1}};
          end
        endcase
      end
  end

`ifdef CUSTOM_PATTERN_SUPPORT
  reg [6:0] custom_cnt;
  reg [19:0] custom_out;
  always @(posedge atxclk or negedge atxrstn)
  begin
    if (!atxrstn)
      begin
        custom_cnt <= 7'h0;
        custom_out <= 20'd0;   //SAR 46547
      end
    else
      begin
        if ((custom_cnt + prbs_txn)>=79 || !custom_sel_sync) custom_cnt <= 7'h0;
        else custom_cnt <= custom_cnt + prbs_txn + 1'b1;
        case (custom_cnt)
          7'd5  : custom_out <= custom_pattern[24:05];
          7'd8  : custom_out <= custom_pattern[27:08];
          7'd10 : custom_out <= custom_pattern[29:10];
          7'd15 : custom_out <= custom_pattern[34:15];
          7'd16 : custom_out <= custom_pattern[35:16];
          7'd20 : custom_out <= custom_pattern[39:20];
          7'd24 : custom_out <= custom_pattern[43:24];
          7'd25 : custom_out <= custom_pattern[44:25];
          7'd30 : custom_out <= custom_pattern[49:30];
          7'd32 : custom_out <= custom_pattern[51:32];
          7'd35 : custom_out <= custom_pattern[54:35];
          7'd40 : custom_out <= custom_pattern[59:40];
          7'd45 : custom_out <= custom_pattern[64:45];
          7'd48 : custom_out <= custom_pattern[67:48];
          7'd50 : custom_out <= custom_pattern[69:50];
          7'd55 : custom_out <= custom_pattern[74:55];
          7'd56 : custom_out <= custom_pattern[75:56];
          7'd60 : custom_out <= custom_pattern[79:60];
          7'd64 : custom_out <= {4'h0,custom_pattern[79:64]};
          7'd65 : custom_out <= {5'h0,custom_pattern[79:65]};
          7'd70 : custom_out <= {10'h0,custom_pattern[79:70]};
          7'd72 : custom_out <= {12'h0,custom_pattern[79:72]};
          7'd75 : custom_out <= {15'h0,custom_pattern[79:75]};
          default : custom_out <= custom_pattern[19:00];
        endcase
      end
  end
`endif

  reg [19:0] prbs_inv;
  always @*
  begin : loop_inv
    integer i;
    for (i=0; i<20;i=i+1) begin
      prbs_inv[i] = ~prbs31[30-i];
    end
  end

  reg [19:0] prbs_out_r;
  always @(posedge atxclk or negedge atxrstn)
  begin
    if (!atxrstn)
      begin
        prbs_out_r <= 20'h0;
      end
    else
      begin
`ifdef CUSTOM_PATTERN_SUPPORT
        prbs_out_r <= (meso_lpbk)? arxdo : (custom_sel_sync)? custom_out : prbs_inv;
`else
        prbs_out_r <= (meso_lpbk)? arxdo : prbs_inv;
`endif
      end
  end

  assign prbs_out = prbs_out_r;


endmodule
