// 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, sweeps through all implementations
// within a certain range to check whether they are max length.

module prim_lfsr_tb;

//////////////////////////////////////////////////////
// config
//////////////////////////////////////////////////////

// this can be overriden on the command line
// supported types are GAL_XOR, FIB_XNOR
`ifdef LFSR_TYPE
  localparam string           LfsrType   = `LFSR_TYPE;
`else
  localparam string           LfsrType   = "GAL_XOR";
`endif
`ifdef MIN_LFSR_DW
  localparam int unsigned     MinLfsrDw  = `MIN_LFSR_DW;
`else
  localparam int unsigned     MinLfsrDw  = 4;
`endif
`ifdef MAX_LFSR_DW
  localparam int unsigned     MaxLfsrDw  = `MAX_LFSR_DW;
`else
  localparam int unsigned     MaxLfsrDw  = 32;
`endif

  // leave this constant
  localparam logic SEED       = 1'b1;

  localparam time  ClkPeriod  = 10000;

//////////////////////////////////////////////////////
// clock
//////////////////////////////////////////////////////

  wire clk, rst_n;

  clk_rst_if main_clk (
    .clk,
    .rst_n
  );

//////////////////////////////////////////////////////
// DUTs
//////////////////////////////////////////////////////

  logic [MaxLfsrDw:0] lfsr_en, err;
  logic [MaxLfsrDw:MinLfsrDw][MaxLfsrDw-1:0] state_out;
  logic [MaxLfsrDw:MinLfsrDw][MaxLfsrDw-1:0] lfsr_periods;

  for (genvar k = MinLfsrDw; k <= MaxLfsrDw; k++) begin : gen_duts
    prim_lfsr #(
      .LfsrType    ( LfsrType ),
      .LfsrDw      ( k        ),
      .EntropyDw   ( 1        ),
      .StateOutDw  ( k        ),
      .DefaultSeed ( k'(SEED) ),
      // enable internal max length check
      .MaxLenSVA   ( 1'b1     )
    ) i_prim_lfsr (
      .clk_i         ( clk                 ),
      .rst_ni        ( rst_n               ),
      .seed_en_i     ( 1'b0                ),
      .seed_i        ( '0                  ),
      .lfsr_en_i     ( lfsr_en[k]          ),
      .entropy_i     ( 1'b0                ),
      .state_o       ( state_out[k][k-1:0] )
    );

    if (k < MaxLfsrDw) begin : gen_tie_off
      assign state_out[k][MaxLfsrDw-1:k] = '0;
    end

    // calculate period of LFSR:
    assign lfsr_periods[k] = MaxLfsrDw'({{(k-1){1'b1}}, 1'b0});
  end

//////////////////////////////////////////////////////
// stimuli application / response checking
//////////////////////////////////////////////////////

  initial begin : p_stimuli
    lfsr_en = '0;
    err     = '0;

    main_clk.set_period_ns(ClkPeriod);
    main_clk.set_active();
    main_clk.apply_reset();

    $display("LFSR maxlen test started for %s (%0d bit to %0d bit).",
        LfsrType, MinLfsrDw, MaxLfsrDw);

    main_clk.wait_clks(10);

    // enable all LFSRs
    lfsr_en = '1;

    $display("Running for 2**%0d-1 cycles...", MaxLfsrDw);
    for (longint unsigned k = 0; k <= lfsr_periods[MaxLfsrDw]; k++ ) begin

      main_clk.wait_clks(1);

      for (int unsigned j = MinLfsrDw; j <= MaxLfsrDw; j++) begin
        // check if we reached the initial state again
        if (state_out[j] == MaxLfsrDw'(SEED) && lfsr_en[j]) begin
          // $display("cycle: %d -- lfsr: %d -- %x ?= %x, %x",
          // k, j, state_out[j], SEED, lfsr_en);
          lfsr_en[j] = 1'b0;
          // we expect this to occur only after the maximum length period
          if (lfsr_periods[j] == k) begin
            $display("Maxlen check for LFSR %0d succeeded!", j);
          end else begin
            err[j] = 1'b1;
            $error("Error LFSR %0d is not maximal length!", j);
          end
        end
      end
    end

    main_clk.wait_clks(10);

    for (int unsigned j = MinLfsrDw; j <= MaxLfsrDw; j++) begin
      if (lfsr_en[j]) begin
        $error("Error LFSR %0d never got back to initial state!", j);
        err[j] = 1'b1;
      end
    end

    if (!err) begin
      $display("All LFSRs from %0d bit to %0d have maximum length!",
          MinLfsrDw, MaxLfsrDw);
      // signature for makefile
      $display("TEST PASSED CHECKS");
    end else begin
      $display("One or more checks have failed!");
      // signature for makefile
      $display("TEST FAILED CHECKS");
    end

    $finish();
  end


endmodule : prim_lfsr_tb
