// DESCRIPTION: Verilator: System Verilog test of case and if
//
// This code instantiates and runs a simple CPU written in System Verilog.
//
// This file ONLY is placed into the Public Domain, for any use, without
// warranty.
// SPDX-License-Identifier: CC0-1.0

// Contributed 2012 by M W Lund, Atmel Corporation and Jeremy Bennett, Embecosm.


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

   input clk;

   /*AUTOWIRE*/

   // **************************************************************************
   // Regs and Wires
   // **************************************************************************

   reg     rst;
   integer rst_count;


   st3_testbench st3_testbench_i (/*AUTOINST*/
                                  // Inputs
                                  .clk                  (clk),
                                  .rst                  (rst));

   // **************************************************************************
   // Reset Generation
   // **************************************************************************

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

   always @( posedge clk ) begin
      if (rst_count < 2) begin
         rst_count++;
      end
      else begin
         rst = 1'b0;
      end
   end

   // **************************************************************************
   // Closing message
   // **************************************************************************

   final begin
      $write("*-* All Finished *-*\n");
   end

endmodule


module st3_testbench (/*AUTOARG*/
   // Inputs
   clk, rst
   );

   input  clk;
   input  rst;

   logic  clk;
   logic  rst;
   logic [8*16-1:0] wide_input_bus;
   logic            decrementA;          // 0=Up-counting, 1=down-counting
   logic            dual_countA;         // Advance counter by 2 steps at a time
   logic            cntA_en;             // Enable Counter A
   logic            decrementB;          // 0=Up-counting, 1=down-counting
   logic            dual_countB;         // Advance counter by 2 steps at a time
   logic            cntB_en;             // Enable counter B
   logic [47:0]     selected_out;
   integer          i;


   initial begin
      decrementA = 1'b0;
      dual_countA = 1'b0;
      cntA_en = 1'b1;
      decrementB = 1'b0;
      dual_countB = 1'b0;
      cntB_en = 1'b1;
      wide_input_bus = {8'hf5,
                         8'hef,
                         8'hd5,
                         8'hc5,
                         8'hb5,
                         8'ha5,
                         8'h95,
                         8'h85,
                         8'ha7,
                         8'ha6,
                         8'ha5,
                         8'ha4,
                         8'ha3,
                         8'ha2,
                         8'ha1,
                         8'ha0};
      i = 0;
   end


   simple_test_3
     simple_test_3_i
       (// Outputs
        .selected_out                    (selected_out[47:0]),
        // Inputs
        .wide_input_bus                  (wide_input_bus[8*16-1:0]),
        .rst                             (rst),
        .clk                             (clk),
        .decrementA                      (decrementA),
        .dual_countA                     (dual_countA),
        .cntA_en                         (cntA_en),
        .decrementB                      (decrementB),
        .dual_countB                     (dual_countB),
        .cntB_en                         (cntB_en));


   // Logic to print outputs and then finish.
   always @(posedge clk) begin
      if (i < 50) begin
`ifdef TEST_VERBOSE
         $display("%x", simple_test_3_i.cntA_reg ,"%x",
                  simple_test_3_i.cntB_reg ," ", "%x", selected_out);
`endif
         i <= i + 1;
      end
      else begin
         $finish();
      end
   end // always @ (posedge clk)

endmodule


// Module testing:
// - Unique case
// - Priority case
// - Unique if
// - ++, --, =- and =+ operands.

module simple_test_3
  (input logic [8*16-1:0] wide_input_bus,
   input logic         rst,
   input logic         clk,
   // Counter A
   input logic         decrementA,  // 0=Up-counting, 1=down-counting
   input logic         dual_countA, // Advance counter by 2 steps at a time
   input logic         cntA_en,     // Enable Counter A
   // Counter B
   input logic         decrementB,  // 0=Up-counting, 1=down-counting
   input logic         dual_countB, // Advance counter by 2 steps at a time
   input logic         cntB_en,     // Enable counter B

   // Outputs
   output logic [47:0] selected_out);

   // Declarations
   logic [3:0]         cntA_reg;       // Registered version of cntA
   logic [3:0]         cntB_reg;       // Registered version of cntA


   counterA
     counterA_inst
       (/*AUTOINST*/
        // Outputs
        .cntA_reg                       (cntA_reg[3:0]),
        // Inputs
        .decrementA                     (decrementA),
        .dual_countA                    (dual_countA),
        .cntA_en                        (cntA_en),
        .clk                            (clk),
        .rst                            (rst));

   counterB
     counterB_inst
       (/*AUTOINST*/
        // Outputs
        .cntB_reg                       (cntB_reg[3:0]),
        // Inputs
        .decrementB                     (decrementB),
        .dual_countB                    (dual_countB),
        .cntB_en                        (cntB_en),
        .clk                            (clk),
        .rst                            (rst));

   simple_test_3a
     sta
       (.wide_input_bus        (wide_input_bus),
        .selector              (cntA_reg),
        .selected_out          (selected_out[7:0]));

   simple_test_3b
     stb
       (.wide_input_bus        (wide_input_bus),
        .selector              (cntA_reg),
        .selected_out          (selected_out[15:8]));

   simple_test_3c
     stc
       (.wide_input_bus        (wide_input_bus),
        .selector              (cntB_reg),
        .selected_out          (selected_out[23:16]));

   simple_test_3d
     std
       (.wide_input_bus        (wide_input_bus),
        .selector              (cntB_reg),
        .selected_out          (selected_out[31:24]));

   simple_test_3e
     ste
       (.wide_input_bus        (wide_input_bus),
        .selector              (cntB_reg),
        .selected_out          (selected_out[39:32]));

   simple_test_3f
     stf
       (.wide_input_bus        (wide_input_bus),
        .selector              (cntB_reg),
        .selected_out          (selected_out[47:40]));


endmodule // simple_test_3


module counterA
  (output logic [3:0]          cntA_reg, // Registered version of cntA
   input logic decrementA,               // 0=Up-counting, 1=down-counting
   input logic dual_countA,              // Advance counter by 2 steps at a time
   input logic cntA_en,                  // Enable Counter A
   input logic clk,                      // Clock
   input logic rst);                     // Synchronous reset



   logic [3:0] cntA;                     // combinational count variable.

   // Counter A
   // Sequential part of counter CntA
   always_ff @(posedge clk)
     begin
        cntA_reg <= cntA;
     end

   // Combinational part of counter
   // Had to be split up to test C-style update, as there are no
   // non-blocking version like -<=
   always_comb
     if (rst)
       cntA = 0;
     else  begin
        cntA = cntA_reg;              // Necessary to avoid latch
        if (cntA_en) begin
           if (decrementA)
             if (dual_countA)
               //cntA = cntA - 2;
               cntA -= 2;
             else
               //cntA = cntA - 1;
               cntA--;
           else
             if (dual_countA)
               //cntA = cntA + 2;
               cntA += 2;
             else
               //cntA = cntA + 1;
               cntA++;
        end // if (cntA_en)
     end
endmodule                             // counterA


module counterB
  (output logic [3:0]          cntB_reg, // Registered version of cntA
   input logic decrementB,               // 0=Up-counting, 1=down-counting
   input logic dual_countB,              // Advance counter by 2 steps at a time
   input logic cntB_en,                  // Enable counter B
   input logic clk,                      // Clock
   input logic rst);                     // Synchronous reset

   // Counter B - tried to write sequential only, but ended up without
   // SystemVerilog.

   always_ff @(posedge clk) begin
      if (rst)
        cntB_reg <= 0;
      else
        if (cntB_en) begin
           if (decrementB)
             if (dual_countB)
               cntB_reg <= cntB_reg - 2;
             else
               cntB_reg <= cntB_reg - 1;
           // Attempts to write in SystemVerilog:
           else
             if (dual_countB)
               cntB_reg <= cntB_reg + 2;
             else
               cntB_reg <= cntB_reg + 1;
           // Attempts to write in SystemVerilog:
        end
   end // always_ff @
endmodule


// A multiplexor in terms of look-up
module simple_test_3a
  (input logic [8*16-1:0] wide_input_bus,
   input logic [3:0]  selector,
   output logic [7:0] selected_out);


   always_comb
     selected_out = {wide_input_bus[selector*8+7],
                     wide_input_bus[selector*8+6],
                     wide_input_bus[selector*8+5],
                     wide_input_bus[selector*8+4],
                     wide_input_bus[selector*8+3],
                     wide_input_bus[selector*8+2],
                     wide_input_bus[selector*8+1],
                     wide_input_bus[selector*8]};

endmodule // simple_test_3a


// A multiplexer in terms of standard case
module simple_test_3b
  (input logic [8*16-1:0] wide_input_bus,
   input logic [3:0]  selector,
   output logic [7:0] selected_out);


   always_comb begin
      case (selector)
        4'h0: selected_out = wide_input_bus[  7:  0];
        4'h1: selected_out = wide_input_bus[ 15:  8];
        4'h2: selected_out = wide_input_bus[ 23: 16];
        4'h3: selected_out = wide_input_bus[ 31: 24];
        4'h4: selected_out = wide_input_bus[ 39: 32];
        4'h5: selected_out = wide_input_bus[ 47: 40];
        4'h6: selected_out = wide_input_bus[ 55: 48];
        4'h7: selected_out = wide_input_bus[ 63: 56];
        4'h8: selected_out = wide_input_bus[ 71: 64];
        4'h9: selected_out = wide_input_bus[ 79: 72];
        4'ha: selected_out = wide_input_bus[ 87: 80];
        4'hb: selected_out = wide_input_bus[ 95: 88];
        4'hc: selected_out = wide_input_bus[103: 96];
        4'hd: selected_out = wide_input_bus[111:104];
        4'he: selected_out = wide_input_bus[119:112];
        4'hf: selected_out = wide_input_bus[127:120];
      endcase // case (selector)

   end

endmodule // simple_test_3b


// A multiplexer in terms of unique case
module simple_test_3c
  (input logic [8*16-1:0] wide_input_bus,
   input logic [3:0]  selector,
   output logic [7:0] selected_out);


   always_comb begin
      unique case (selector)
        4'h0: selected_out = wide_input_bus[  7:  0];
        4'h1: selected_out = wide_input_bus[ 15:  8];
        4'h2: selected_out = wide_input_bus[ 23: 16];
        4'h3: selected_out = wide_input_bus[ 31: 24];
        4'h4: selected_out = wide_input_bus[ 39: 32];
        4'h5: selected_out = wide_input_bus[ 47: 40];
        4'h6: selected_out = wide_input_bus[ 55: 48];
        4'h7: selected_out = wide_input_bus[ 63: 56];
        4'h8: selected_out = wide_input_bus[ 71: 64];
        4'h9: selected_out = wide_input_bus[ 79: 72];
        4'ha: selected_out = wide_input_bus[ 87: 80];
        4'hb: selected_out = wide_input_bus[ 95: 88];
        4'hc: selected_out = wide_input_bus[103: 96];
        4'hd: selected_out = wide_input_bus[111:104];
        4'he: selected_out = wide_input_bus[119:112];
        4'hf: selected_out = wide_input_bus[127:120];
      endcase // case (selector)

   end

endmodule // simple_test_3c


// A multiplexer in terms of unique if
module simple_test_3d
  (input logic [8*16-1:0] wide_input_bus,
   input logic [3:0]  selector,
   output logic [7:0] selected_out);


   always_comb begin
      unique if (selector == 4'h0) selected_out = wide_input_bus[  7:  0];
      else if (selector == 4'h1) selected_out = wide_input_bus[ 15:  8];
      else if (selector == 4'h2) selected_out = wide_input_bus[ 23: 16];
      else if (selector == 4'h3) selected_out = wide_input_bus[ 31: 24];
      else if (selector == 4'h4) selected_out = wide_input_bus[ 39: 32];
      else if (selector == 4'h5) selected_out = wide_input_bus[ 47: 40];
      else if (selector == 4'h6) selected_out = wide_input_bus[ 55: 48];
      else if (selector == 4'h7) selected_out = wide_input_bus[ 63: 56];
      else if (selector == 4'h8) selected_out = wide_input_bus[ 71: 64];
      else if (selector == 4'h9) selected_out = wide_input_bus[ 79: 72];
      else if (selector == 4'ha) selected_out = wide_input_bus[ 87: 80];
      else if (selector == 4'hb) selected_out = wide_input_bus[ 95: 88];
      else if (selector == 4'hc) selected_out = wide_input_bus[103: 96];
      else if (selector == 4'hd) selected_out = wide_input_bus[111:104];
      else if (selector == 4'he) selected_out = wide_input_bus[119:112];
      else if (selector == 4'hf) selected_out = wide_input_bus[127:120];
   end

endmodule // simple_test_3d


// Test of priority case
// Note: This does NOT try to implement the same function as above.
module simple_test_3e
  (input logic [8*16-1:0] wide_input_bus,
   input logic [3:0]  selector,
   output logic [7:0] selected_out);


   always_comb begin
      priority case (1'b1)
        selector[0]: selected_out = wide_input_bus[  7:  0]; // Bit 0 has highets priority
        selector[2]: selected_out = wide_input_bus[ 39: 32]; // Note 2 higher priority than 1
        selector[1]: selected_out = wide_input_bus[ 23: 16]; // Note 1 lower priority than 2
        selector[3]: selected_out = wide_input_bus[ 71: 64]; // Bit 3 has lowest priority
        default:     selected_out = wide_input_bus[127:120]; // for selector = 0.
      endcase // case (selector)

   end

endmodule // simple_test_3e


// Test of "inside"
// Note: This does NOT try to implement the same function as above.
// Note: Support for "inside" is a separate Verilator feature request, so is
//       not used inside a this version of the test.
module simple_test_3f
  (input logic [8*16-1:0] wide_input_bus,
   input logic [3:0]  selector,
   output logic [7:0] selected_out);


   always_comb begin
/* -----\/----- EXCLUDED -----\/-----
      if ( selector[3:0] inside { 4'b?00?, 4'b1100})      // Matching 0000, 0001, 1000, 1100, 1001
        // if ( selector[3:2] inside { 2'b?0, selector[1:0]})
        selected_out = wide_input_bus[  7:  0];
      else
 -----/\----- EXCLUDED -----/\----- */
      /* verilator lint_off CASEOVERLAP */
        priority casez (selector[3:0])
          4'b0?10: selected_out = wide_input_bus[ 15:  8]; // Matching 0010 and 0110
          4'b0??0: selected_out = wide_input_bus[ 23: 16]; // Overlap: only 0100 remains (0000 in "if" above)
          4'b0100: selected_out = wide_input_bus[ 31: 24]; // Overlap: Will never occur
          default: selected_out = wide_input_bus[127:120];   // Remaining 0011,0100,0101,0111,1010,1011,1101,1110,1111
        endcase // case (selector)
      /* verilator lint_on CASEOVERLAP */
   end

endmodule // simple_test_3f
