// SPDX-License-Identifier: Apache-2.0

package firrtlTests

import firrtl.transforms.InlineAcrossCastsTransform
import firrtl.testutils.FirrtlFlatSpec
import firrtl.testutils.FirrtlCheckers._

class InlineAcrossCastsEquivalenceSpec extends FirrtlFlatSpec {
  /*
   * Note: InlineCasts is still part of mverilog, so this test must both:
   * - Test that the InlineCasts fix is effective given the current mverilog
   * - Provide a test that will be robust if and when InlineCasts is no longer run in mverilog
   *
   * This is why the test passes InlineCasts as a custom transform: to future-proof it so that
   * it can do real LEC against no-InlineCasts. It currently is just a sanity check that the
   * emitted Verilog is legal, but it will automatically become a more meaningful test when
   * InlineCasts is not run in mverilog.
   */
  "InlineCastsTransform" should "not produce broken Verilog" in {
    val input =
      s"""circuit literalsel_fir:
         |  module literalsel_fir:
         |    input i: UInt<4>
         |    output o: SInt<8>
         |    o <= pad(asSInt(UInt<2>("h1")), 8)
         |""".stripMargin
    firrtlEquivalenceTest(input, Seq(new InlineAcrossCastsTransform))
  }

  it should "not inline complex expressions into other complex expressions" in {
    val input =
      """circuit NeverInlineComplexIntoComplex :
        |  module NeverInlineComplexIntoComplex :
        |    input a : SInt<3>
        |    input b : UInt<2>
        |    input c : UInt<2>
        |    input sel : UInt<1>
        |    output out : SInt<3>
        |    node diff = sub(b, c)
        |    out <= mux(sel, a, asSInt(diff))
        |""".stripMargin
    val expected =
      """module NeverInlineComplexIntoComplexRef(
        |  input  [2:0] a,
        |  input  [1:0] b,
        |  input  [1:0] c,
        |  input        sel,
        |  output [2:0] out
        |);
        |  wire [2:0] diff = b - c;
        |  assign out = sel ? $signed(a) : $signed(diff);
        |endmodule
        |""".stripMargin
    firrtlEquivalenceWithVerilog(input, expected)
  }

  it should "inline casts on both sides of a more complex expression" in {
    val input =
      """circuit test :
        |  module test :
        |    input clock : Clock
        |    input in : UInt<8>
        |    output out : UInt<8>
        |
        |    node _T_1 = asUInt(clock)
        |    node _T_2 = not(_T_1)
        |    node clock_n = asClock(_T_2)
        |    reg r : UInt<8>, clock_n
        |    r <= in
        |    out <= r
        |""".stripMargin
    val verilog = compileToVerilogCircuitState(input)
    verilog should containLine("always @(posedge clock_n) begin")

  }
}
