// DESCRIPTION: Verilator: FSM coverage grouped reset semantics test
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0

// Group accepted simulator-style reset semantics, including reset include /
// exclude behavior and nearby fallback cases that should still preserve FSMs.

module fsm_reset_policy (
    input logic clk,
    input logic rst
);

  typedef enum logic [0:0] {
    S0 = 1'b0,
    S1 = 1'b1
  } state_t;

  state_t state_incl  /*verilator fsm_reset_arc*/;
  state_t state_excl;

  always_ff @(posedge clk) begin
    if (rst) state_incl <= S0;
    else
      case (state_incl)
        S0: state_incl <= S1;
        default: state_incl <= S1;
      endcase
  end

  always_ff @(posedge clk) begin
    if (rst) state_excl <= S0;
    else
      case (state_excl)
        S0: state_excl <= S1;
        default: state_excl <= S1;
      endcase
  end
endmodule

module fsm_reset_other_assign_ok (
    input logic clk,
    input logic rst
);
  typedef enum logic [1:0] {
    S0 = 2'b00,
    S1 = 2'b01,
    S2 = 2'b10
  } state_t;

  logic aux;
  logic start;
  state_t state_q;
  state_t state_d;

  always_ff @(posedge clk) begin
    if (rst) begin
      aux <= 1'b1;
      start <= 1'b1;
      state_q <= S0;
      aux <= 1'b0;
    end else begin
      state_q <= state_d;
    end
  end

  always_comb begin
    state_d = state_q;
    case (state_q)
      S0: state_d = start ? S1 : S2;
      default: state_d = S0;
    endcase
  end
endmodule

module fsm_oneblock_reset_mismatch_ok (
    input logic clk,
    input logic rst
);
  typedef enum logic [1:0] {
    S0 = 2'd0,
    S1 = 2'd1,
    S2 = 2'd2
  } state_t;

  state_t state_q = S0;
  state_t other_q = S2;

  always_ff @(posedge clk) begin
    if (rst) begin
      other_q <= S0;
    end else begin
      case (state_q)
        S0: state_q <= S1;
        default: state_q <= S2;
      endcase
    end
  end
endmodule

module fsm_oneblock_reset_nonconst_ok (
    input logic clk,
    input logic rst
);
  typedef enum logic [1:0] {
    S0 = 2'd0,
    S1 = 2'd1,
    S2 = 2'd2
  } state_t;

  state_t state_q = S0;
  state_t other_q = S2;

  // Useful because the one-block reset fallback must ignore non-constant reset
  // assignments and still retain the supported case(state_q) FSM below.
  always_ff @(posedge clk) begin
    if (rst) begin
      state_q <= other_q;
    end else begin
      case (state_q)
        S0: state_q <= S1;
        default: state_q <= S2;
      endcase
    end
  end
endmodule

module t (
    input logic clk
);

  logic rst;
  integer cyc;

  fsm_reset_policy reset_policy_u (.clk(clk), .rst(rst));
  fsm_reset_other_assign_ok reset_other_assign_ok_u (.clk(clk), .rst(rst));
  fsm_oneblock_reset_mismatch_ok oneblock_reset_mismatch_ok_u (.clk(clk), .rst(rst));
  fsm_oneblock_reset_nonconst_ok oneblock_reset_nonconst_ok_u (.clk(clk), .rst(rst));

  initial begin
    rst = 1'b1;
    cyc = 0;
  end

  always @(posedge clk) begin
    cyc <= cyc + 1;
    if (cyc == 1) rst <= 1'b0;
    if (cyc == 5) begin
      $write("*-* All Finished *-*\n");
      $finish;
    end
  end

endmodule
