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

module t (/*AUTOARG*/
   // Inputs
   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 ();

   input clk;
   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
         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
      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
