summaryrefslogtreecommitdiff
path: root/src/test/scala/chiselTests/AnnotatingExample.scala
blob: 0be3ba596bb8ca80b72751b17194e2efb2216735 (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
// See LICENSE for license details.

package chiselTests

import chisel3._
import chisel3.core.Module
import chisel3.internal.InstanceId
import chisel3.testers.BasicTester
import org.scalatest._

import scala.util.DynamicVariable

//scalastyle:off magic.number

/**
  * This Spec file illustrates use of Donggyu's component name API, it currently only
  * uses three methods .signalName, .parentModName and .pathName
  *
  * This is also an illustration of how to implement an annotation system in chisel3
  * A local (my) Driver and Builder are created to provide thread-local access to
  * an annotation map, and then a post elaboration annotation processor can resolve
  * the keys and could serialize the annotations to a file for use by firrtl passes
  */

class SomeSubMod(param1: Int, param2: Int) extends Module {
  val io = new Bundle {
    val in = Input(UInt(16.W))
    val out = Output(SInt(32.W))
  }
  val annotate = MyBuilder.myDynamicContext.annotationMap

  annotate(AnnotationKey(this, JustThisRef))   = s"SomeSubMod($param1, $param2)"
  annotate(AnnotationKey(io.in, AllRefs))      = "sub mod io.in"
  annotate(AnnotationKey(io.out, JustThisRef)) = "sub mod io.out"
}

class AnnotatingExample extends Module {
  val io = new Bundle {
    val a  = Input(UInt(32.W))
    val b  = Input(UInt(32.W))
    val e  = Input(Bool())
    val z  = Output(UInt(32.W))
    val v  = Output(Bool())
    val bun = new Bundle {
      val nested_1 = Input(UInt(12.W))
      val nested_2 = Output(Bool())
    }
  }
  val x = Reg(UInt(32.W))
  val y = Reg(UInt(32.W))

  val subModule1 = Module(new SomeSubMod(1, 2))
  val subModule2 = Module(new SomeSubMod(3, 4))


  val annotate = MyBuilder.myDynamicContext.annotationMap

  annotate(AnnotationKey(subModule2, AllRefs))     = s"SomeSubMod was used"

  annotate(AnnotationKey(x, JustThisRef)) = "I am register X"
  annotate(AnnotationKey(y, AllRefs)) = "I am register Y"
  annotate(AnnotationKey(io.a, JustThisRef)) = "I am io.a"
  annotate(AnnotationKey(io.bun.nested_1, AllRefs)) = "I am io.bun.nested_1"
  annotate(AnnotationKey(io.bun.nested_2, JustThisRef)) = "I am io.bun.nested_2"
}

class AnnotatingExampleTester extends BasicTester {
  val dut = Module(new AnnotatingExample)

  stop()
}

class AnnotatingExampleSpec extends FlatSpec with Matchers {
  behavior of "Annotating components of a circuit"

  it should "contain the following relative keys" in {
    val annotationMap = MyDriver.buildAnnotatedCircuit { () => new AnnotatingExampleTester }

    annotationMap.contains("SomeSubMod.io.in") should be(true)
    annotationMap.contains("AnnotatingExample.y") should be(true)

    annotationMap("SomeSubMod.io.in") should be("sub mod io.in")
  }
  it should "contain the following absolute keys" in {
    val annotationMap = MyDriver.buildAnnotatedCircuit { () => new AnnotatingExampleTester }

    annotationMap.contains("AnnotatingExampleTester.dut.subModule2.io.out") should be (true)
    annotationMap.contains("AnnotatingExampleTester.dut.x") should be (true)

    annotationMap("AnnotatingExampleTester.dut.subModule2.io.out") should be ("sub mod io.out")
  }
}

trait AnnotationScope
case object AllRefs     extends AnnotationScope
case object JustThisRef extends AnnotationScope

object AnnotationKey {
  def apply(component: InstanceId): AnnotationKey = {
    AnnotationKey(component, AllRefs)
  }
}
case class AnnotationKey(val component: InstanceId, scope: AnnotationScope) {
  override def toString: String = {
    scope match {
      case JustThisRef =>
        s"${component.pathName}"
      case AllRefs =>
        s"${component.parentModName}.${component.instanceName}"
      case  _ =>
        s"${component.toString}_unknown_scope"
    }
  }
}

class AnnotationMap extends scala.collection.mutable.HashMap[AnnotationKey, String]

class MyDynamicContext {
  val annotationMap = new AnnotationMap
}

object MyBuilder {
  private val myDynamicContextVar = new DynamicVariable[Option[MyDynamicContext]](None)

  def myDynamicContext: MyDynamicContext =
    myDynamicContextVar.value getOrElse new MyDynamicContext

  def processAnnotations(annotationMap: AnnotationMap): Map[String, String] = {
    annotationMap.map { case (k,v) => k.toString -> v}.toMap
  }

  def build[T <: Module](f: => T): Map[String, String] = {
    myDynamicContextVar.withValue(Some(new MyDynamicContext)) {
      Driver.emit(() => f)
      processAnnotations(myDynamicContextVar.value.get.annotationMap)
    }
  }
}

object MyDriver extends BackendCompilationUtilities {
  /**
    * illustrates a chisel3 style driver that, annotations can only processed within this structure
    */
  def buildAnnotatedCircuit[T <: Module](gen: () => T): Map[String, String] = MyBuilder.build(gen())
}