// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Testbench module for prim_lfsr. Intended to be used with a formal tool.

module prim_lfsr_tb #(
  // LFSR entropy and output bitwidths (set to 1 here as they are unused)
  parameter int unsigned EntropyDw        = 1,
  parameter int unsigned StateOutDw       = 1,
  // this specifies the range of differently
  // parameterized LFSRs to instantiate and check
  parameter int unsigned GalXorMinLfsrDw  = 4,
  parameter int unsigned GalXorMaxLfsrDw  = 64,
  parameter int unsigned FibXnorMinLfsrDw = 3,
  parameter int unsigned FibXnorMaxLfsrDw = 168,
  // LFSRs up to this bitwidth are checked for maximum length
  parameter int unsigned MaxLenSVAThresh  = 10,
  // derived params
  localparam int unsigned GalMaxGtFibMax  = GalXorMaxLfsrDw > FibXnorMaxLfsrDw,
  localparam int unsigned MaxLfsrDw       = GalXorMaxLfsrDw * GalMaxGtFibMax +
                                            FibXnorMaxLfsrDw * (1 - GalMaxGtFibMax),
  localparam int unsigned NumDuts         = 2*(FibXnorMaxLfsrDw - FibXnorMinLfsrDw + 1) +
                                            2*(GalXorMaxLfsrDw - GalXorMinLfsrDw + 1)
) (
  input                                      clk_i,
  input                                      rst_ni,
  input [NumDuts-1:0]                        load_ext_en_i,
  input [NumDuts-1:0][MaxLfsrDw-1:0]         seed_ext_i,
  input [NumDuts-1:0]                        lfsr_en_i,
  input [NumDuts-1:0][EntropyDw-1:0]         entropy_i,
  output logic [NumDuts-1:0][StateOutDw-1:0] state_o
);

  for (genvar k = GalXorMinLfsrDw; k <= GalXorMaxLfsrDw; k++) begin : gen_gal_xor_duts
    localparam int unsigned Idx = k - GalXorMinLfsrDw;
    prim_lfsr #(.LfsrType("GAL_XOR"),
      .LfsrDw      ( k          ),
      .EntropyDw   ( EntropyDw  ),
      .StateOutDw  ( StateOutDw ),
      .DefaultSeed ( 1          ),
      // disabled for large LFSRs since this becomes prohibitive in runtime
      .MaxLenSVA   ( k <= MaxLenSVAThresh )
    ) u_prim_lfsr (
      .clk_i,
      .rst_ni,
      .seed_en_i ( load_ext_en_i[Idx]  ),
      .seed_i    ( k'(seed_ext_i[Idx]) ),
      .lfsr_en_i ( lfsr_en_i[Idx]      ),
      .entropy_i ( entropy_i[Idx]      ),
      .state_o   ( state_o[Idx]        )
    );
  end

  // same as above but with non-linear output enabled.
  // only power-of-two widths are allowed.
  for (genvar k = 16; k <= GalXorMaxLfsrDw; k = k*2)
  begin : gen_gal_xor_duts_nonlinear
    localparam int unsigned Idx = k - GalXorMinLfsrDw;
    prim_lfsr #(.LfsrType("GAL_XOR"),
      .LfsrDw      ( k          ),
      .EntropyDw   ( EntropyDw  ),
      .StateOutDw  ( StateOutDw ),
      .DefaultSeed ( 1          ),
      // disabled for large LFSRs since this becomes prohibitive in runtime
      .MaxLenSVA   ( k <= MaxLenSVAThresh ),
      .NonLinearOut (1)
    ) u_prim_lfsr (
      .clk_i,
      .rst_ni,
      .seed_en_i ( load_ext_en_i[Idx]  ),
      .seed_i    ( k'(seed_ext_i[Idx]) ),
      .lfsr_en_i ( lfsr_en_i[Idx]      ),
      .entropy_i ( entropy_i[Idx]      ),
      .state_o   ( state_o[Idx]        )
    );
  end

  for (genvar k = FibXnorMinLfsrDw; k <= FibXnorMaxLfsrDw; k++) begin : gen_fib_xnor_duts
    localparam int unsigned Idx = k - FibXnorMinLfsrDw + GalXorMaxLfsrDw - GalXorMinLfsrDw + 1;
    prim_lfsr #(.LfsrType("FIB_XNOR"),
      .LfsrDw      ( k          ),
      .EntropyDw   ( EntropyDw  ),
      .StateOutDw  ( StateOutDw ),
      .DefaultSeed ( 1          ),
      // disabled for large LFSRs since this becomes prohibitive in runtime
      .MaxLenSVA   ( k <= MaxLenSVAThresh )
    ) u_prim_lfsr (
      .clk_i,
      .rst_ni,
      .seed_en_i ( load_ext_en_i[Idx]  ),
      .seed_i    ( k'(seed_ext_i[Idx]) ),
      .lfsr_en_i ( lfsr_en_i[Idx]      ),
      .entropy_i ( entropy_i[Idx]      ),
      .state_o   ( state_o[Idx]        )
    );
  end

  // same as above but with non-linear output enabled.
  // only power-of-two widths are allowed.
  for (genvar k = 16; k <= FibXnorMaxLfsrDw; k = k*2)
  begin : gen_fib_xnor_duts_nonlinear
    localparam int unsigned Idx = k - FibXnorMinLfsrDw + GalXorMaxLfsrDw - GalXorMinLfsrDw + 1;
    prim_lfsr #(.LfsrType("FIB_XNOR"),
      .LfsrDw      ( k          ),
      .EntropyDw   ( EntropyDw  ),
      .StateOutDw  ( StateOutDw ),
      .DefaultSeed ( 1          ),
      // disabled for large LFSRs since this becomes prohibitive in runtime
      .MaxLenSVA   ( k <= MaxLenSVAThresh ),
      .NonLinearOut (1)
    ) u_prim_lfsr (
      .clk_i,
      .rst_ni,
      .seed_en_i ( load_ext_en_i[Idx]  ),
      .seed_i    ( k'(seed_ext_i[Idx]) ),
      .lfsr_en_i ( lfsr_en_i[Idx]      ),
      .entropy_i ( entropy_i[Idx]      ),
      .state_o   ( state_o[Idx]        )
    );
  end

endmodule : prim_lfsr_tb
