// See LICENSE for license details. package firrtlTests import org.scalatest.FlatSpec import org.scalatest.Matchers import org.scalatest.junit.JUnitRunner import firrtl.ir.Circuit import firrtl.{ ChirrtlForm, CircuitState, Compiler, HighFirrtlCompiler, MiddleFirrtlCompiler, LowFirrtlCompiler, Parser, VerilogCompiler } /** * An example methodology for testing Firrtl compilers. * * Given an input Firrtl circuit (expressed as a string), * the compiler is executed. The output of the compiler * should be compared against the check string. */ abstract class CompilerSpec extends FlatSpec { def parse (s: String): Circuit = Parser.parse(s.split("\n").toIterator) def compiler: Compiler def input: String def check: String def getOutput: String = { val res = compiler.compileAndEmit(CircuitState(parse(input), ChirrtlForm)) res.getEmittedCircuit.value } } /** * An example test for testing the HighFirrtlCompiler. * * Given an input Firrtl circuit (expressed as a string), * the compiler is executed. The output of the compiler * is parsed again and compared (in-memory) to the parsed * input. */ class HighFirrtlCompilerSpec extends CompilerSpec with Matchers { val compiler = new HighFirrtlCompiler() val input = """circuit Top : module Top : input a : UInt<1>[2] node x = a """ val check = input "Any circuit" should "match exactly to its input" in { (parse(getOutput)) should be (parse(check)) } } /** * An example test for testing the MiddleFirrtlCompiler. * * Given an input Firrtl circuit (expressed as a string), * the compiler is executed. The output of the compiler is * a lowered (to MidForm) version of the input circuit. The output is * string compared to the correct lowered circuit. */ class MiddleFirrtlCompilerSpec extends CompilerSpec with Matchers { val compiler = new MiddleFirrtlCompiler() val input = """ circuit Top : module Top : input reset : UInt<1> input a : UInt<1>[2] wire b : UInt b <= a[0] when reset : b <= UInt(0) """ // Verify that Vecs are retained, but widths are inferred and whens are expanded. val check = Seq( "circuit Top :", " module Top :", " input reset : UInt<1>", " input a : UInt<1>[2]", " wire b : UInt<1>", " node _GEN_0 = mux(reset, UInt<1>(\"h0\"), a[0])", " b <= _GEN_0\n\n" ).reduce(_ + "\n" + _) "A circuit" should "match exactly to its MidForm state" in { (parse(getOutput)) should be (parse(check)) } } /** * An example test for testing the LoweringCompiler. * * Given an input Firrtl circuit (expressed as a string), * the compiler is executed. The output of the compiler is * a lowered version of the input circuit. The output is * string compared to the correct lowered circuit. */ class LowFirrtlCompilerSpec extends CompilerSpec with Matchers { val compiler = new LowFirrtlCompiler() val input = """ circuit Top : module Top : input a : UInt<1>[2] node x = a """ val check = Seq( "circuit Top :", " module Top :", " input a_0 : UInt<1>", " input a_1 : UInt<1>", " node x_0 = a_0", " node x_1 = a_1\n\n" ).reduce(_ + "\n" + _) "A circuit" should "match exactly to its lowered state" in { (parse(getOutput)) should be (parse(check)) } } /** * An example test for testing the VerilogCompiler. * * Given an input Firrtl circuit (expressed as a string), * the compiler is executed. The output of the compiler is * the corresponding Verilog. The output is string compared * to the correct Verilog. */ class VerilogCompilerSpec extends CompilerSpec with Matchers { val compiler = new VerilogCompiler() val input = """ circuit Top : module Top : input a : UInt<1>[2] output b : UInt<1>[2] b <= a """ val check = Seq( "`ifdef RANDOMIZE_GARBAGE_ASSIGN", "`define RANDOMIZE", "`endif", "`ifdef RANDOMIZE_INVALID_ASSIGN", "`define RANDOMIZE", "`endif", "`ifdef RANDOMIZE_REG_INIT", "`define RANDOMIZE", "`endif", "`ifdef RANDOMIZE_MEM_INIT", "`define RANDOMIZE", "`endif", "", "module Top(", " input a_0,", " input a_1,", " output b_0,", " output b_1", ");", " assign b_0 = a_0;", " assign b_1 = a_1;", "endmodule\n" ).reduce(_ + "\n" + _) "A circuit's verilog output" should "match the given string" in { getOutput should be (check) } }