// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0

module t
  (/*AUTOARG*/
   // Inputs
   clk
   );

  input clk;

  int   cyc = 0;


  // Test for https://github.com/verilator/verilator/issues/3364
  // Make sure all SV queue API is supported and verilator can generate
  // compile-able C++ models for it.

  // simple queue
  logic [31:0] my_int_queue [$];

  // On the functions and tasks, the my_int_queue.pop_[front|back]() call will
  // have nodep->firstAbovep() != nullptr. Because the pop_front or pop_back is
  // the first node on the "list".
  // To fix this, V3Width.cpp will not use firstAbovep(), and instead us
  // isStandalongStmt() -- which checks if the pop_front or pop_back is
  // 2nd or later, or if it's first in the list that it's in a "block" of code.
  // For functions/tasks, that is checked with:
  // VN_IS(backp(), NodeFTask)=True, so even though
  function automatic void f_pop_back__my_int_queue();
    void'(my_int_queue.pop_back());
  endfunction : f_pop_back__my_int_queue

  function automatic void f_pop_front__my_int_queue();
    void'(my_int_queue.pop_front());
  endfunction : f_pop_front__my_int_queue

  task automatic t_pop_back__my_int_queue();
    void'(my_int_queue.pop_back());
  endtask : t_pop_back__my_int_queue

  task automatic t_pop_front__my_int_queue();
    void'(my_int_queue.pop_front());
  endtask : t_pop_front__my_int_queue


  task automatic do_random_queue_operation();
    bit [7:0] rand_op;
    int       rand_index;
    logic [31:0] item;


    rand_op = 8'($urandom_range(32, 0));
    case(rand_op)
    8'd0: ; // nop

    // pushes (2x of these)
    8'd1, 8'd2: my_int_queue.push_back($urandom);
    8'd3, 8'd4: my_int_queue.push_front($urandom);

    // delete:
    8'd5: my_int_queue.delete();

    // insert(index, item):
    8'd6: begin
      rand_index = $urandom_range(my_int_queue.size());
      my_int_queue.insert(rand_index, item);
    end

    // shuffle
    8'd7: my_int_queue.shuffle();

    // Various pops for rand_op >= 8:
    // pops to var
    // V3Width debug -- firstAbovep()=ASSIGN (which I guess does the ; for us
    //                                        so we don't need the queue op to
    //                                        do it.)
    // isStandalongStmt() will ignore ASSIGN, return false (NodeAssign is
    // child of AstNodeStmt)
    8'd8: if (my_int_queue.size() > 0) item = my_int_queue.pop_front();
    8'd9: if (my_int_queue.size() > 0) item = my_int_queue.pop_back();

    // pops to the void
    // V3Width debug -- firstAbovep()=IF
    // This is fixed with isStandalongStmt() -- VN_IS(backp(), NodeIf)=True
    8'd10: if (my_int_queue.size() > 0) void'(my_int_queue.pop_front());
    8'd11: if (my_int_queue.size() > 0) void'(my_int_queue.pop_back());

    // pop result to the lhs of a condition, and do something with it.
    8'd12:
      if (my_int_queue.size() > 0)
        // V3Width debug -- firstAbovep()=LTE (good we don't want a ; here)
        if (my_int_queue.pop_front() <= 2022)
          my_int_queue.push_front(3022); // living in the year 3022.

    // pop result to the rhs of a condition, and do something with it.
    8'd13:
      if (my_int_queue.size() > 0)
        // V3Width debug -- firstAbovep()=GT (good we don't want a ; here)
        if (4022 > my_int_queue.pop_front())
          my_int_queue.push_front(3023); // living in the year 3023.

    // pops to the void after yet another case:
    // V3Width debug -- firstAbovep()=CASEITEM (not a nullptr)
    // This is fixed with isStandalongStmt() -- VN_IS(backp(), CaseItem)=True
    8'd14:
      case (my_int_queue.size() > 0)
      0: ;
      1: void'(my_int_queue.pop_front());
      default: ;
      endcase // case (my_int_queue.size() > 0)

    // V3Width debug -- firstAbovep()=CASEITEM (not a nullptr)
    // backp()->nextp()=CASEITEM (different one)
    // This is fixed with isStandalongStmt() -- VN_IS(backp(), CaseItem)=True
    8'd15:
      case (my_int_queue.size() > 0)
      0: ;
      1: void'(my_int_queue.pop_back());
      default;
      endcase // case (my_int_queue.size() > 0)

    // pops in a function or task
    8'd16: if (my_int_queue.size() > 0) f_pop_back__my_int_queue();
    8'd17: if (my_int_queue.size() > 0) f_pop_front__my_int_queue();
    8'd18: if (my_int_queue.size() > 0) t_pop_back__my_int_queue();
    8'd19: if (my_int_queue.size() > 0) t_pop_front__my_int_queue();

    // But what if we put some dummy code before the pop_back() or pop_front():
    8'd20: begin
      if (my_int_queue.size() > 0) begin
        ; // dummy line
        // V3Width debug -- firstAbovep()=BEGIN (is not nullptr).
        // This is fixed with isStandalongStmt() -- VN_IS(backp(), NodeIf)=True
        void'(my_int_queue.pop_back());
      end
    end
    8'd21: begin
      automatic int temp_int = 0;
      if (my_int_queue.size() > 0) begin
        temp_int = 5; // dummy line
        // V3Width debug -- firstAbovep()=nullptr (good)
        void'(my_int_queue.pop_back());
      end
    end
    8'd22: begin
      if (my_int_queue.size() > 0) begin
        automatic int some_temp_dummy_int;
        some_temp_dummy_int = 42;
        // V3Width debug -- firstAbovep()=nullptr (good)
        void'(my_int_queue.pop_back());
      end
    end
    8'd23: begin
      if (my_int_queue.size() > 0) begin
        // no dummy here, just a 'begin' helper before it.
        // V3Width debug -- firstAbovep()=BEGIN (is not nullptr).
        // This is fixed with isStandalongStmt() -- VN_IS(backp(), NodeIf)=True
        void'(my_int_queue.pop_back());
      end
    end

    // What about an if of something else, followed by a pop_front?
    8'd24: begin
      automatic int temp_int = 0;
      if (my_int_queue.size() == 0) begin // dummy
        temp_int = 1000;
      end
      void'(my_int_queue.pop_front()); // firstAbovep() should be nullptr here.
    end


    default: ; // nop
    endcase // case (rand_op)

  endtask : do_random_queue_operation



  always @ (posedge clk) begin : main
    cyc <= cyc + 1;

    do_random_queue_operation();

    if (cyc > 100) begin
      $write("*-* All Finished *-*\n");
      $finish();
    end
  end



endmodule : t
