// 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 under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2012 M W Lund, Atmel Corporation and Jeremy Bennett, Embecosm
// SPDX-License-Identifier: CC0-1.0

module t (
    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
