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

module t (
    input clk
);

  // verilator lint_off MULTIDRIVEN

  wire [31:0] outb0c0;
  wire [31:0] outb0c1;
  wire [31:0] outb1c0;
  wire [31:0] outb1c1;

  reg [7:0] lclmem[7:0];

  ma ma0 (
      .outb0c0(outb0c0),
      .outb0c1(outb0c1),
      .outb1c0(outb1c0),
      .outb1c1(outb1c1)
  );

  global_mod #(32'hf00d) global_cell ();
  global_mod #(32'hf22d) global_cell2 ();

  integer cyc = 1;
  always @(posedge clk) begin
    cyc <= cyc + 1;
`ifdef TEST_VERBOSE
    $write("[%0t] cyc%0d: %0x %0x %0x %0x\n", $time, cyc, outb0c0, outb0c1, outb1c0, outb1c1);
`endif
    if (cyc == 2) begin
      if (global_cell.globali != 32'hf00d) $stop;
      if (global_cell2.globali != 32'hf22d) $stop;
      if ($root.t.global_cell.globali != 32'hf00d) $stop;
      if ($root.t.global_cell2.globali != 32'hf22d) $stop;
      if (outb0c0 != 32'h00) $stop;
      if (outb0c1 != 32'h01) $stop;
      if (outb1c0 != 32'h10) $stop;
      if (outb1c1 != 32'h11) $stop;
    end
    if (cyc == 3) begin
      // Can we scope down and read and write vars?
      ma0.mb0.mc0.out <= ma0.mb0.mc0.out + 32'h100;
      ma0.mb0.mc1.out <= ma0.mb0.mc1.out + 32'h100;
      ma0.mb1.mc0.out <= ma0.mb1.mc0.out + 32'h100;
      ma0.mb1.mc1.out <= ma0.mb1.mc1.out + 32'h100;
    end
    if (cyc == 4) begin
      // Can we do dotted's inside array sels?
      ma0.rmtmem[ma0.mb0.mc0.out[2:0]] = 8'h12;
      lclmem[ma0.mb0.mc0.out[2:0]] = 8'h24;
      if (outb0c0 != 32'h100) $stop;
      if (outb0c1 != 32'h101) $stop;
      if (outb1c0 != 32'h110) $stop;
      if (outb1c1 != 32'h111) $stop;
    end
    if (cyc == 5) begin
      if (ma0.rmtmem[ma0.mb0.mc0.out[2:0]] != 8'h12) $stop;
      if (lclmem[ma0.mb0.mc0.out[2:0]] != 8'h24) $stop;
      if (outb0c0 != 32'h1100) $stop;
      if (outb0c1 != 32'h2101) $stop;
      if (outb1c0 != 32'h2110) $stop;
      if (outb1c1 != 32'h3111) $stop;
    end
    if (cyc == 6) begin
      if (outb0c0 != 32'h31100) $stop;
      if (outb0c1 != 32'h02101) $stop;
      if (outb1c0 != 32'h42110) $stop;
      if (outb1c1 != 32'h03111) $stop;
    end
    if (cyc == 9) begin
      $write("*-* All Finished *-*\n");
      $finish;
    end
  end

endmodule

`ifdef USE_INLINE_MID
`define INLINE_MODULE /*verilator inline_module*/
`define INLINE_MID_MODULE /*verilator no_inline_module*/
`else
`ifdef USE_INLINE
`define INLINE_MODULE /*verilator inline_module*/
`define INLINE_MID_MODULE /*verilator inline_module*/
`else
`define INLINE_MODULE /*verilator public_module*/
`define INLINE_MID_MODULE /*verilator public_module*/
`endif
`endif

module global_mod;
  `INLINE_MODULE
  parameter INITVAL = 0;
  integer globali;
  initial globali = INITVAL;
endmodule

module ma (
    output wire [31:0] outb0c0,
    output wire [31:0] outb0c1,
    output wire [31:0] outb1c0,
    output wire [31:0] outb1c1
);
  `INLINE_MODULE

  reg [7:0] rmtmem[7:0];

  mb #(0) mb0 (
      .outc0(outb0c0),
      .outc1(outb0c1)
  );
  mb #(1) mb1 (
      .outc0(outb1c0),
      .outc1(outb1c1)
  );
endmodule

module mb (
    output wire [31:0] outc0,
    output wire [31:0] outc1
);
  `INLINE_MID_MODULE
  parameter P2 = 0;
  mc #(P2, 0) mc0 (.out(outc0));
  mc #(P2, 1) mc1 (.out(outc1));
  global_mod #(32'hf33d) global_cell2 ();

  wire reach_up_clk = t.clk;
  always @(reach_up_clk) begin
    if (P2 == 0) begin  // Only for mb0
      // verilog_format: off
      if (outc0 !== t.ma0.mb0.mc0.out) $stop; // Top module name and lower instances
      if (outc0 !==   ma0.mb0.mc0.out) $stop; // Upper module name and lower instances
      if (outc0 !==   ma .mb0.mc0.out) $stop; // Upper module name and lower instances
      if (outc0 !==        mb.mc0.out) $stop; // This module name and lower instances
      if (outc0 !==       mb0.mc0.out) $stop; // Upper instance name and lower instances
      if (outc0 !==           mc0.out) $stop; // Lower instances

      if (outc1 !== t.ma0.mb0.mc1.out) $stop; // Top module name and lower instances
      if (outc1 !==   ma0.mb0.mc1.out) $stop; // Upper module name and lower instances
      if (outc1 !==   ma .mb0.mc1.out) $stop; // Upper module name and lower instances
      if (outc1 !==        mb.mc1.out) $stop; // This module name and lower instances
      if (outc1 !==       mb0.mc1.out) $stop; // Upper instance name and lower instances
      if (outc1 !==           mc1.out) $stop; // Lower instances
      // verilog_format: on
    end
  end
endmodule

module mc (
    output reg [31:0] out
);
  `INLINE_MODULE
  parameter P2 = 0;
  parameter P3 = 0;
  initial begin
    out = {24'h0, P2[3:0], P3[3:0]};
    //$write("%m P2=%0x p3=%0x out=%x\n",P2, P3, out);
  end

  // Can we look from the top module name down?
  wire [31:0] reach_up_cyc = t.cyc;

  always @(posedge t.clk) begin
    //$write("[%0t] %m: Got reachup, cyc=%0d\n", $time, reach_up_cyc);
    if (reach_up_cyc == 2) begin
      if (global_cell.globali != 32'hf00d) $stop;
      if (global_cell2.globali != 32'hf33d) $stop;
    end
    if (reach_up_cyc == 4) begin
      out[15:12] <= {P2[3:0] + P3[3:0] + 4'd1};
    end
    if (reach_up_cyc == 5) begin
      // Can we set another instance?
      if (P3 == 1) begin  // Without this, there are two possible correct answers...
        mc0.out[19:16] <= {mc0.out[19:16] + P2[3:0] + P3[3:0] + 4'd2};
        $display("%m Set %x->%x   %x %x %x %x", mc0.out, {mc0.out[19:16] + P2[3:0] + P3[3:0] + 4'd2
                 }, mc0.out[19:16], P2[3:0], P3[3:0], 4'd2);
      end
    end
  end
endmodule
