// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Hardened LFSR module that instantiates two LFSRs of the same type.
// The state vector of both LFSRs is constantly checked and an error is asserted if the
// two states are inconsistent.

module prim_double_lfsr #(
  // prim_lfsr parameters - refer to prim_lfsr for their meaning/
  parameter                    LfsrType     = "GAL_XOR",
  parameter int unsigned       LfsrDw       = 32,
  localparam int unsigned      LfsrIdxDw    = $clog2(LfsrDw),
  parameter int unsigned       EntropyDw    =  8,
  parameter int unsigned       StateOutDw   =  8,
  parameter logic [LfsrDw-1:0] DefaultSeed  = LfsrDw'(1),
  parameter logic [LfsrDw-1:0] CustomCoeffs = '0,
  parameter bit                StatePermEn  = 1'b0,
  parameter logic [LfsrDw-1:0][LfsrIdxDw-1:0] StatePerm = '0,
  parameter bit                MaxLenSVA    = 1'b1,
  parameter bit                LockupSVA    = 1'b1,
  parameter bit                ExtSeedSVA   = 1'b1,
  parameter bit                NonLinearOut = 1'b0,
  // This should only be disabled in special circumstances, for example
  // in non-comportable IPs where an error does not trigger an alert.
  parameter bit                EnableAlertTriggerSVA = 1
) (
  input                         clk_i,
  input                         rst_ni,
  input                         seed_en_i,
  input        [LfsrDw-1:0]     seed_i,
  input                         lfsr_en_i,
  input        [EntropyDw-1:0]  entropy_i,
  output logic [StateOutDw-1:0] state_o,
  // Asserted if the parallel LFSR states are inconsistent.
  output logic                  err_o
);


  logic [1:0][LfsrDw-1:0] lfsr_state;
  // We employ redundant LFSRs to guard against FI attacks.
  for (genvar k = 0; k < 2; k++) begin : gen_double_lfsr
    // Instantiate size_only buffers to prevent
    // optimization / merging of redundant logic.
    logic lfsr_en_buf, seed_en_buf;
    logic [EntropyDw-1:0] entropy_buf;
    logic [LfsrDw-1:0] seed_buf, lfsr_state_unbuf;
    prim_buf #(
      .Width(EntropyDw + LfsrDw + 2)
    ) u_prim_buf_input (
      .in_i({seed_en_i, seed_i, lfsr_en_i, entropy_i}),
      .out_o({seed_en_buf, seed_buf, lfsr_en_buf, entropy_buf})
    );

    prim_lfsr #(
      .LfsrType(LfsrType),
      .LfsrDw(LfsrDw),
      .EntropyDw(EntropyDw),
      // output the full width so that the states can be cross checked.
      .StateOutDw(LfsrDw),
      .DefaultSeed(DefaultSeed),
      .CustomCoeffs(CustomCoeffs),
      .StatePermEn(StatePermEn),
      .StatePerm(StatePerm),
      .MaxLenSVA(MaxLenSVA),
      .LockupSVA(LockupSVA),
      .ExtSeedSVA(ExtSeedSVA),
      .NonLinearOut(NonLinearOut)
    ) u_prim_lfsr (
      .clk_i,
      .rst_ni,
      .seed_en_i  ( seed_en_buf      ),
      .seed_i     ( seed_buf         ),
      .lfsr_en_i  ( lfsr_en_buf      ),
      .entropy_i  ( entropy_buf      ),
      .state_o    ( lfsr_state_unbuf )
    );

    prim_buf #(
      .Width(LfsrDw)
    ) u_prim_buf_output (
      .in_i(lfsr_state_unbuf),
      .out_o(lfsr_state[k])
    );
  end

`ifdef SIMULATION
`ifndef VERILATOR
  // Ensure both LFSRs start off with the same default seed. if randomized in simulations.
  initial begin : p_sync_lfsr_default_seed
    wait (!$isunknown(gen_double_lfsr[0].u_prim_lfsr.DefaultSeedLocal));
    wait (!$isunknown(gen_double_lfsr[1].u_prim_lfsr.DefaultSeedLocal));
    gen_double_lfsr[1].u_prim_lfsr.DefaultSeedLocal =
        gen_double_lfsr[0].u_prim_lfsr.DefaultSeedLocal;
    $display("%m: Updated gen_double_lfsr[1].u_prim_lfsr.DefaultSeedLocal = 0x%0h",
        gen_double_lfsr[1].u_prim_lfsr.DefaultSeedLocal);
  end
`endif
`endif

  // Output the state from the first LFSR
  assign state_o = lfsr_state[0][StateOutDw-1:0];
  assign err_o = lfsr_state[0] != lfsr_state[1];

  // This logic that will be assign to one, when user adds macro
  // ASSERT_PRIM_DOUBLE_LFSR_ERROR_TRIGGER_ALERT to check the error with alert, in case that
  // prim_double_lfsr is used in design without adding this assertion check.
  `ifdef INC_ASSERT
  logic unused_assert_connected;

  `ASSERT_INIT_NET(AssertConnected_A, unused_assert_connected === 1'b1 || !EnableAlertTriggerSVA)
  `endif
endmodule : prim_double_lfsr
