aboutsummaryrefslogtreecommitdiff
path: root/src/test/scala/firrtlTests/AnnotationTests.scala
blob: 77f0778131b80f32b419568c6a82a3cd39d4737b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// See LICENSE for license details.

package firrtlTests

import java.io.{File, FileWriter, Writer}

import firrtl.annotations.AnnotationYamlProtocol._
import firrtl.annotations._
import firrtl._
import firrtl.passes.InlineAnnotation
import firrtl.passes.memlib.PinAnnotation
import net.jcazevedo.moultingyaml._
import org.scalatest.Matchers

/**
 * An example methodology for testing Firrtl annotations.
 */
trait AnnotationSpec extends LowTransformSpec {
  // Dummy transform
  def transform = new CustomResolveAndCheck(LowForm)

  // Check if Annotation Exception is thrown
  override def failingexecute(annotations: AnnotationMap, input: String): Exception = {
    intercept[AnnotationException] {
      compile(CircuitState(parse(input), ChirrtlForm, Some(annotations)), Seq.empty)
    }
  }
  def execute(annotations: AnnotationMap, input: String, check: Annotation): Unit = {
    val cr = compile(CircuitState(parse(input), ChirrtlForm, Some(annotations)), Seq.empty)
    cr.annotations.get.annotations should contain (check)
  }
}


/**
 * Tests for Annotation Permissibility and Tenacity
 *
 * WARNING(izraelevitz): Not a complete suite of tests, requires the LowerTypes
 * pass and ConstProp pass to correctly populate its RenameMap before Strict, Rigid, Firm,
 * Unstable, Fickle, and Insistent can be tested.
 */
class AnnotationTests extends AnnotationSpec with Matchers {
  def getAMap (a: Annotation): AnnotationMap = AnnotationMap(Seq(a))
  val input: String =
    """circuit Top :
       |  module Top :
       |    input a : UInt<1>[2]
       |    input b : UInt<1>
       |    node c = b""".stripMargin
  val mName = ModuleName("Top", CircuitName("Top"))
  val aName = ComponentName("a", mName)
  val bName = ComponentName("b", mName)
  val cName = ComponentName("c", mName)

  "Loose and Sticky annotation on a node" should "pass through" in {
    val ta = Annotation(cName, classOf[Transform], "")
    execute(getAMap(ta), input, ta)
  }

  "Annotations" should "be readable from file" in {
    val annotationStream = getClass.getResourceAsStream("/annotations/SampleAnnotations.anno")
    val annotationsYaml = scala.io.Source.fromInputStream(annotationStream).getLines().mkString("\n").parseYaml
    val annotationArray = annotationsYaml.convertTo[Array[Annotation]]
    annotationArray.length should be (9)
    annotationArray(0).targetString should be ("ModC")
    annotationArray(7).transformClass should be ("firrtl.passes.InlineInstances")
    val expectedValue = "TopOfDiamond\nWith\nSome new lines"
    annotationArray(7).value should be (expectedValue)
  }

  "Badly formatted serializations" should "return reasonable error messages" in {
    var badYaml =
      """
        |- transformClass: firrtl.passes.InlineInstances
        |  targetString: circuit.module..
        |  value: ModC.this params 16 32
      """.stripMargin.parseYaml

    var thrown = intercept[Exception] {
      badYaml.convertTo[Array[Annotation]]
    }
    thrown.getMessage should include ("Illegal component name")

    badYaml =
      """
        |- transformClass: firrtl.passes.InlineInstances
        |  targetString: .circuit.module.component
        |  value: ModC.this params 16 32
      """.stripMargin.parseYaml

    thrown = intercept[Exception] {
      badYaml.convertTo[Array[Annotation]]
    }
    thrown.getMessage should include ("Illegal circuit name")
  }

  "Round tripping annotations through text file" should "preserve annotations" in {
    val annos: Array[Annotation] = Seq(
      InlineAnnotation(CircuitName("fox")),
      InlineAnnotation(ModuleName("dog", CircuitName("bear"))),
      InlineAnnotation(ComponentName("chocolate", ModuleName("like", CircuitName("i")))),
      PinAnnotation(CircuitName("Pinniped"), Seq("sea-lion", "monk-seal"))
    ).toArray

    val annoFile = new File("temp-anno")
    val writer = new FileWriter(annoFile)
    writer.write(annos.toYaml.prettyPrint)
    writer.close()

    val yaml = io.Source.fromFile(annoFile).getLines().mkString("\n").parseYaml
    annoFile.delete()

    val readAnnos = yaml.convertTo[Array[Annotation]]

    annos.zip(readAnnos).foreach { case (beforeAnno, afterAnno) =>
      beforeAnno.targetString should be (afterAnno.targetString)
      beforeAnno.target should be (afterAnno.target)
      beforeAnno.transformClass should be (afterAnno.transformClass)
      beforeAnno.transform should be (afterAnno.transform)

      beforeAnno should be (afterAnno)
    }
  }

  "Deleting annotations" should "create a DeletedAnnotation" in {
    val compiler = new VerilogCompiler
    val input =
     """circuit Top :
        |  module Top :
        |    input in: UInt<3>
        |""".stripMargin
    class DeletingTransform extends Transform {
      val inputForm = LowForm
      val outputForm = LowForm
      def execute(state: CircuitState) = state.copy(annotations = None)
    }
    val anno = InlineAnnotation(CircuitName("Top"))
    val annoOpt = Some(AnnotationMap(Seq(anno)))
    val result = compiler.compile(CircuitState(parse(input), ChirrtlForm, annoOpt), Seq(new DeletingTransform))
    result.annotations.get.annotations.head should matchPattern {
      case DeletedAnnotation(x, anno) =>
    }
    val exception = (intercept[FIRRTLException] {
      result.getEmittedCircuit
    })
    val deleted = result.deletedAnnotations
    exception.str should be (s"No EmittedCircuit found! Did you delete any annotations?\n$deleted")
  }
}