// See LICENSE for license details. package firrtlTests import org.scalatest._ import firrtl._ import org.scalacheck.Gen import org.scalacheck.Prop.forAll class ParserSpec extends FirrtlFlatSpec { private object MemTests { val prelude = Seq("circuit top :", " module top :", " mem m : ") val fields = Map("data-type" -> "UInt<32>", "depth" -> "4", "read-latency" -> "1", "write-latency" -> "1", "reader" -> "a", "writer" -> "b", "readwriter" -> "c" ) def fieldsToSeq(m: Map[String, String]): Seq[String] = m map { case (k,v) => s" ${k} => ${v}" } toSeq } private object RegTests { val prelude = Seq("circuit top :", " module top :") val reg = " reg r : UInt<32>, clock" val reset = "reset => (radReset, UInt(\"hdeadbeef\"))" } private object KeywordTests { val prelude = Seq("circuit top :", " module top :") val keywords = Seq("circuit", "module", "extmodule", "parameter", "input", "output", "UInt", "SInt", "Analog", "Fixed", "flip", "Clock", "wire", "reg", "reset", "with", "mem", "depth", "reader", "writer", "readwriter", "inst", "of", "node", "is", "invalid", "when", "else", "stop", "printf", "skip", "old", "new", "undefined", "mux", "validif", "cmem", "smem", "mport", "infer", "read", "write", "rdwr") ++ PrimOps.listing } // ********** Memories ********** "Memories" should "allow arbitrary ordering of fields" in { val fields = MemTests.fieldsToSeq(MemTests.fields) val golden = firrtl.Parser.parse((MemTests.prelude ++ fields)) fields.permutations foreach { permutation => val circuit = firrtl.Parser.parse((MemTests.prelude ++ permutation)) assert(golden === circuit) } } it should "have exactly one of each: data-type, depth, read-latency, and write-latency" in { import MemTests._ def parseWithoutField(s: String) = firrtl.Parser.parse((prelude ++ fieldsToSeq(fields - s))) def parseWithDuplicate(k: String, v: String) = firrtl.Parser.parse((prelude ++ fieldsToSeq(fields) :+ s" ${k} => ${v}")) Seq("data-type", "depth", "read-latency", "write-latency") foreach { field => evaluating { parseWithoutField(field) } should produce [ParameterNotSpecifiedException] evaluating { parseWithDuplicate(field, fields(field)) } should produce [ParameterRedefinedException] } } // ********** Registers ********** "Registers" should "allow no implicit reset" in { import RegTests._ firrtl.Parser.parse((prelude :+ reg)) } it should "allow same-line reset" in { import RegTests._ firrtl.Parser.parse((prelude :+ s"${reg} with : (${reset})" :+ " wire a : UInt")) } it should "allow multi-line reset" in { import RegTests._ firrtl.Parser.parse((prelude :+ s"${reg} with :\n (${reset})")) } // ********** Keywords ********** "Keywords" should "be allowed as Ids" in { import KeywordTests._ keywords foreach { keyword => firrtl.Parser.parse((prelude :+ s" wire ${keyword} : UInt")) } } it should "be allowed on lhs in connects" in { import KeywordTests._ keywords foreach { keyword => firrtl.Parser.parse((prelude ++ Seq(s" wire ${keyword} : UInt", s" ${keyword} <= ${keyword}"))) } } } class ParserPropSpec extends FirrtlPropSpec { // Disable shrinking on error. import org.scalacheck.Shrink implicit val noShrinkString = Shrink[String](_ => Stream.empty) def legalStartChar = Gen.frequency((1, '_'), (20, Gen.alphaChar)) def legalChar = Gen.frequency((1, Gen.numChar), (1, '$'), (10, legalStartChar)) def identifier = for { x <- legalStartChar xs <- Gen.listOf(legalChar) } yield (x :: xs).mkString property("Identifiers should allow [A-Za-z0-9_$] but not allow starting with a digit or $") { forAll (identifier) { id => whenever(id.nonEmpty) { val input = s""" |circuit Test : | module Test : | input $id : UInt<32> |""".stripMargin firrtl.Parser.parse(input split "\n") } } } def bundleField = for { xs <- Gen.nonEmptyListOf(legalChar) } yield xs.mkString property("Bundle fields should allow [A-Za-z0-9_] including starting with a digit or $") { forAll (identifier, bundleField) { case (id, field) => whenever(id.nonEmpty && field.nonEmpty) { val input = s""" |circuit Test : | module Test : | input $id : { $field : UInt<32> } |""".stripMargin firrtl.Parser.parse(input split "\n") } } } }