// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2024 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0

`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d:  got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d:  got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
`define checks(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d:  got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);



module t(clk);
  input clk;

  logic [31:0] cyc = 0;
  always @(posedge clk) begin
    cyc <= cyc + 1;
    if (cyc == 99) begin
      $write("*-* All Finished *-*\n");
      $finish;
    end
  end

  reg [63:0] crc = 64'h5aef0c8d_d70a4497;
  always @ (posedge clk) crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]};

`define at_posedge_clk_on_cycle(n) always @(posedge clk) if (cyc == n)

  // Case 1: narrow packed variable, whole element updates only - 1D
  typedef logic [31:0] elem1_t;
  typedef elem1_t array1_t[128];
  array1_t array1;
  `at_posedge_clk_on_cycle(0) begin
    for (int i = 0 ; i < 128; ++i) array1[i] = 0;
    for (int i = 0 ; i < 128; ++i) `checkh(array1[i], 0);
  end
  `at_posedge_clk_on_cycle(1) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array1[i], 0);
    for (int i = 0 ; i < 128; ++i) array1[i] <= i;
    for (int i = 0 ; i < 128; ++i) `checkh(array1[i], 0);
  end
  `at_posedge_clk_on_cycle(2) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array1[i], i);
    for (int i = 0 ; i < 128; ++i) array1[i] <= ~i;
    for (int i = 0 ; i < 128; ++i) `checkh(array1[i], i);
  end
  `at_posedge_clk_on_cycle(3) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array1[i], ~i);
    for (int i = 0 ; i < 128; ++i) array1[i] <= -1;
    for (int i = 0 ; i < 128; ++i) `checkh(array1[i], ~i);
  end
  `at_posedge_clk_on_cycle(4) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array1[i], -1);
  end

  // Case 2: wide packed variable, whole element updates only - 1D
  typedef logic [127:0] elem2_t;
  typedef elem2_t array2_t[128];
  array2_t array2;
  `at_posedge_clk_on_cycle(0) begin
    for (int i = 0 ; i < 128; ++i) array2[i] = 0;
    for (int i = 0 ; i < 128; ++i) `checkh(array2[i], 0);
  end
  `at_posedge_clk_on_cycle(1) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array2[i], 0);
    for (int i = 0 ; i < 128; ++i) array2[i] <= {4{i}};
    for (int i = 0 ; i < 128; ++i) `checkh(array2[i], 0);
  end
  `at_posedge_clk_on_cycle(2) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array2[i], {4{i}});
    for (int i = 0 ; i < 128; ++i) array2[i] <= {4{~i}};
    for (int i = 0 ; i < 128; ++i) `checkh(array2[i], {4{i}});
  end
  `at_posedge_clk_on_cycle(3) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array2[i], {4{~i}});
    for (int i = 0 ; i < 128; ++i) array2[i] <= '1;
    for (int i = 0 ; i < 128; ++i) `checkh(array2[i], {4{~i}});
  end
  `at_posedge_clk_on_cycle(4) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array2[i], ~128'b0);
  end


  // Case 3: wide packed variable, whole element updates only - 2D
  typedef logic [127:0] elem3_t;
  typedef elem3_t array3sub_t[512];
  typedef array3sub_t array3_t[128];
  array3_t array3;
  `at_posedge_clk_on_cycle(0) begin
    for (int i = 0 ; i < 128; ++i) for (int j = 0 ; j < 512; ++j) array3[i][j] = 0;
    for (int i = 0 ; i < 128; ++i) for (int j = 0 ; j < 512; ++j) `checkh(array3[i][j], 0);
  end
  `at_posedge_clk_on_cycle(1) begin
    for (int i = 0 ; i < 128; ++i) for (int j = 0 ; j < 512; ++j) `checkh(array3[i][j], 0);
    for (int i = 0 ; i < 128; ++i) for (int j = 0 ; j < 512; ++j) array3[i][j] <= {4{i}};
    for (int i = 0 ; i < 128; ++i) for (int j = 0 ; j < 512; ++j) `checkh(array3[i][j], 0);
  end
  `at_posedge_clk_on_cycle(2) begin
    for (int i = 0 ; i < 128; ++i) for (int j = 0 ; j < 512; ++j) `checkh(array3[i][j], {4{i}});
    for (int i = 0 ; i < 128; ++i) for (int j = 0 ; j < 512; ++j) array3[i][j] <= {4{~i}};
    for (int i = 0 ; i < 128; ++i) for (int j = 0 ; j < 512; ++j) `checkh(array3[i][j], {4{i}});
  end
  `at_posedge_clk_on_cycle(3) begin
    for (int i = 0 ; i < 128; ++i) for (int j = 0 ; j < 512; ++j) `checkh(array3[i][j], {4{~i}});
    for (int i = 0 ; i < 128; ++i) for (int j = 0 ; j < 512; ++j) array3[i][j] <= '1;
    for (int i = 0 ; i < 128; ++i) for (int j = 0 ; j < 512; ++j) `checkh(array3[i][j], {4{~i}});
  end
  `at_posedge_clk_on_cycle(4) begin
    for (int i = 0 ; i < 128; ++i) for (int j = 0 ; j < 512; ++j) `checkh(array3[i][j], ~128'b0);
  end

  // Case 4: real
  typedef real elem4_t;
  typedef elem4_t array4_t[128];
  array4_t array4;
  `at_posedge_clk_on_cycle(0) begin
    for (int i = 0 ; i < 128; ++i) array4[i] = 1e-5;
    for (int i = 0 ; i < 128; ++i) `checkr(array4[i], 1e-5);
  end
  `at_posedge_clk_on_cycle(1) begin
    for (int i = 0 ; i < 128; ++i) `checkr(array4[i], 1e-5);
    for (int i = 0 ; i < 128; ++i) array4[i] <= 3.14*real'(i);
    for (int i = 0 ; i < 128; ++i) `checkr(array4[i], 1e-5);
  end
  `at_posedge_clk_on_cycle(2) begin
    for (int i = 0 ; i < 128; ++i) `checkr(array4[i], 3.14*real'(i));
    for (int i = 0 ; i < 128; ++i) array4[i] <= 2.78*real'(i);
    for (int i = 0 ; i < 128; ++i) `checkr(array4[i], 3.14*real'(i));
  end
  `at_posedge_clk_on_cycle(3) begin
    for (int i = 0 ; i < 128; ++i) `checkr(array4[i], 2.78*real'(i));
    for (int i = 0 ; i < 128; ++i) array4[i] <= 1e50;
    for (int i = 0 ; i < 128; ++i) `checkr(array4[i], 2.78*real'(i));
  end
  `at_posedge_clk_on_cycle(4) begin
    for (int i = 0 ; i < 128; ++i) `checkr(array4[i], 1e50);
  end

  // Case 5: narrow packed variable, partial element updates - 1D
  typedef logic [31:0] elem5_t;
  typedef elem5_t array5_t[128];
  array5_t array5;
  `at_posedge_clk_on_cycle(0) begin
    for (int i = 0 ; i < 128; ++i) array5[i] = -1;
    for (int i = 0 ; i < 128; ++i) `checkh(array5[i], -1);
  end
  `at_posedge_clk_on_cycle(1) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array5[i], -1);
    for (int i = 0 ; i < 128; ++i) array5[i][0] <= 1'b0;
    for (int i = 0 ; i < 128; ++i) array5[i][1] <= 1'b0;
    for (int i = 0 ; i < 128; ++i) array5[i][2] <= 1'b0;
    for (int i = 0 ; i < 128; ++i) array5[i][1] <= 1'b1;
    for (int i = 0 ; i < 128; ++i) `checkh(array5[i], -1);
  end
  `at_posedge_clk_on_cycle(2) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array5[i], 32'hffff_fffa);
    for (int i = 0 ; i < 128; ++i) array5[i][18:16] <=  i[3:1];
    for (int i = 0 ; i < 128; ++i) array5[i][19:17] <= ~i[2:0];
    for (int i = 0 ; i < 128; ++i) `checkh(array5[i], 32'hffff_fffa);
  end
  `at_posedge_clk_on_cycle(3) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array5[i], {12'hfff, ~i[2:0], i[1], 16'hfffa});
    for (int i = 0 ; i < 128; ++i) array5[i] <= -1;
    for (int i = 0 ; i < 128; ++i) `checkh(array5[i], {12'hfff, ~i[2:0], i[1], 16'hfffa});
  end
  `at_posedge_clk_on_cycle(4) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array5[i], -1);
  end

  // Case 6: wide packed variable, partial element updates - 1D
  typedef logic [99:0] elem6_t;
  typedef elem6_t array6_t[128];
  array6_t array6;
  `at_posedge_clk_on_cycle(0) begin
    for (int i = 0 ; i < 128; ++i) array6[i] = -1;
    for (int i = 0 ; i < 128; ++i) `checkh(array6[i], -1);
  end
  `at_posedge_clk_on_cycle(1) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array6[i], -1);
    for (int i = 0 ; i < 128; ++i) array6[i][80] <= 1'b0;
    for (int i = 0 ; i < 128; ++i) array6[i][81] <= 1'b0;
    for (int i = 0 ; i < 128; ++i) array6[i][82] <= 1'b0;
    for (int i = 0 ; i < 128; ++i) array6[i][81] <= 1'b1;
    for (int i = 0 ; i < 128; ++i) `checkh(array6[i], -1);
  end
  `at_posedge_clk_on_cycle(2) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array6[i], 100'hf_fffa_ffff_ffff_ffff_ffff_ffff);
    for (int i = 0 ; i < 128; ++i) array6[i][86:84] <= ~i[3:1];
    for (int i = 0 ; i < 128; ++i) array6[i][87:85] <=  i[2:0];
    for (int i = 0 ; i < 128; ++i) `checkh(array6[i], 100'hf_fffa_ffff_ffff_ffff_ffff_ffff);
  end
  `at_posedge_clk_on_cycle(3) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array6[i], {12'hfff, i[2:0], ~i[1], 84'ha_ffff_ffff_ffff_ffff_ffff});
    for (int i = 0 ; i < 128; ++i) array6[i] <= -1;
    for (int i = 0 ; i < 128; ++i) `checkh(array6[i], {12'hfff, i[2:0], ~i[1], 84'ha_ffff_ffff_ffff_ffff_ffff});
  end
  `at_posedge_clk_on_cycle(4) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array6[i], -1);
  end

  // Case 7: variable partial updates
  typedef logic [99:0] elem7_t;
  typedef elem7_t array7sub_t[256];
  typedef array7sub_t array7_t[128];
  array7_t array7_nba;
  array7_t array7_ref;
  always @(posedge clk) begin
    if (cyc == 0) begin
      for (int i = 0 ; i < 128; ++i) for (int j = 0 ; j < 256; ++j) array7_nba[i][j] =  100'b0;
      for (int i = 0 ; i < 128; ++i) for (int j = 0 ; j < 256; ++j) array7_ref[i][j] = ~100'b0;
    end else begin
      for (int i = 0 ; i < 128; ++i) for (int j = 0 ; j < 256; ++j) `checkh(array7_nba[i][j], ~array7_ref[i][j]);
      for (int i = 0 ; i < 128; ++i) begin
        for (int j = 0 ; j < 256; ++j) begin
            array7_nba[i[6:0] ^ crc[30+:7]][j[7:0] ^ crc[10+:8]][7'((crc % 10) * 5) +: 5] <= ~crc[4+:5];
            array7_ref[i[6:0] ^ crc[30+:7]][j[7:0] ^ crc[10+:8]][7'((crc % 10) * 5) +: 5]  =  crc[4+:5];
        end
      end
    end
  end

  // Case 8: Mixed dynamic/non-dynamic
  typedef longint elem8_t;
  typedef elem8_t array8_t[4];
  array8_t array8;
  `at_posedge_clk_on_cycle(0) begin
      array8[0] <= 0;
      array8[1] <= 0;
      array8[2] <= 0;
      array8[3] <= 0;
  end
  `at_posedge_clk_on_cycle(1) begin
      `checkh(array8[0], 0);
      `checkh(array8[1], 0);
      `checkh(array8[2], 0);
      `checkh(array8[3], 0);
      array8[1] <= 42;
      array8[3] <= 63;
      for (int i = 1 ; i < 3 ; ++i) array8[i] <= 2*i + 7;
      array8[1] <= 74;
  end
  `at_posedge_clk_on_cycle(3) begin
      `checkh(array8[0], 0);
      `checkh(array8[1], 74);
      `checkh(array8[2], 11);
      `checkh(array8[3], 63);
  end

  // Case 9: string
  typedef string elem9_t;
  typedef elem9_t array9_t[10];
  array9_t array9;
  `at_posedge_clk_on_cycle(0) begin
    for (int i = 0 ; i < 10; ++i) array9[i] = "squid";
    for (int i = 0 ; i < 10; ++i) `checks(array9[i], "squid");
  end
  `at_posedge_clk_on_cycle(1) begin
    for (int i = 0 ; i < 10; ++i) `checks(array9[i], "squid");
    for (int i = 0 ; i < 10; ++i) array9[i] <= "octopus";
    for (int i = 0 ; i < 10; ++i) `checks(array9[i], "squid");
  end
  `at_posedge_clk_on_cycle(2) begin
    for (int i = 0 ; i < 10; ++i) `checks(array9[i], "octopus");
    for (int i = 1 ; i < 9; ++i) begin
      string tmp;
      $sformat(tmp, "%0d-legged-cephalopod", i);
      array9[i] <= tmp;
    end
    for (int i = 0 ; i < 10; ++i) `checks(array9[i], "octopus");
  end
  `at_posedge_clk_on_cycle(3) begin
    `checks(array9[0], "octopus");
    `checks(array9[1], "1-legged-cephalopod");
    `checks(array9[2], "2-legged-cephalopod");
    `checks(array9[3], "3-legged-cephalopod");
    `checks(array9[4], "4-legged-cephalopod");
    `checks(array9[5], "5-legged-cephalopod");
    `checks(array9[6], "6-legged-cephalopod");
    `checks(array9[7], "7-legged-cephalopod");
    `checks(array9[8], "8-legged-cephalopod");
    `checks(array9[9], "octopus");
    for (int i = 0 ; i < 10; ++i) array9[i] <= "cuttlefish";
    `checks(array9[0], "octopus");
    `checks(array9[1], "1-legged-cephalopod");
    `checks(array9[2], "2-legged-cephalopod");
    `checks(array9[3], "3-legged-cephalopod");
    `checks(array9[4], "4-legged-cephalopod");
    `checks(array9[5], "5-legged-cephalopod");
    `checks(array9[6], "6-legged-cephalopod");
    `checks(array9[7], "7-legged-cephalopod");
    `checks(array9[8], "8-legged-cephalopod");
    `checks(array9[9], "octopus");
  end
  `at_posedge_clk_on_cycle(4) begin
    for (int i = 0 ; i < 10; ++i) `checks(array9[i], "cuttlefish");
  end

  // Case 10: Packed narrow, but whole array also target of NBA directly
  typedef logic [31:0] elem10_t;
  typedef elem10_t array10_t[128];
  array10_t array10;
  array10_t array10_init = '{default: 10};
  `at_posedge_clk_on_cycle(0) begin
    for (int i = 0 ; i < 128; ++i) array10[i] = 0;
    for (int i = 0 ; i < 128; ++i) `checkh(array10[i], 0);
  end
  `at_posedge_clk_on_cycle(1) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array10[i], 0);
    array10 <= array10_init;
    for (int i = 0 ; i < 128; ++i) `checkh(array10[i], 0);
  end
  `at_posedge_clk_on_cycle(2) begin
    for (int i =  0 ; i < 128; ++i) `checkh(array10[i], 10);
    for (int i = 64 ; i < 128; ++i) array10[i][4] <= 1'b1;
    for (int i =  0 ; i < 128; ++i) `checkh(array10[i], 10);
  end
  `at_posedge_clk_on_cycle(3) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array10[i], i < 64 ? 10 : 26);
    for (int i = 0 ; i < 128; ++i) array10[i] <= ~i;
    for (int i = 0 ; i < 128; ++i) `checkh(array10[i], i < 64 ? 10 : 26);
  end
  `at_posedge_clk_on_cycle(4) begin
    for (int i = 0 ; i < 128; ++i) `checkh(array10[i], ~i);
  end

endmodule
