Skip to content

Commit

Permalink
Add the ability to render printf statements (#24)
Browse files Browse the repository at this point in the history
- default behavior is not to do it
- added test to show it being included or not
- Fix help message for --dot-timeout-seconds
- Reduce size of literals bigger than 32 characters
  • Loading branch information
chick authored Sep 27, 2019
1 parent 950ffd9 commit 6c1b995
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 7 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,5 @@ for a good introduction to writing Firrtl transforms
## TODO
- This used to work by annotating a circuit, consider re-adding that
- Setting to allow the graphs to go deeper into sub-module logic
- Big firrtl modules that take more than 7 seconds to render are stopped, try and fix this.
- Provide links to the chisel source

16 changes: 12 additions & 4 deletions src/main/scala/dotvisualizer/FirrtlDiagrammer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ case class RankDirAnnotation(rankDir: String) extends OptionAnnotation

case object UseRankAnnotation extends OptionAnnotation

case object ShowPrintfsAnnotation extends OptionAnnotation

object FirrtlDiagrammer {

var dotTimeOut = 7
Expand Down Expand Up @@ -156,7 +158,7 @@ object FirrtlDiagrammer {
config.firrtlSource
}
else {
io.Source.fromFile(config.firrtlSourceFile).getLines().mkString("\n")
FileUtils.getText(config.firrtlSourceFile)
}
}

Expand Down Expand Up @@ -224,9 +226,13 @@ object FirrtlDiagrammer {
.action { (_, c) => c.copy(useRanking = true) }
.text("tries to rank elements by depth from inputs")

opt[Unit]('p', "show-printfs")
.action { (_, c) => c.copy(showPrintfs = true) }
.text("render printfs showing arguments")

opt[Int]('s', "dot-timeout-seconds")
.action { (x, c) => c.copy(dotTimeOut = x) }
.text("use this to only see the top level view")
.text("gives up trying to diagram a module after 7 seconds, this increases that time")
}

parser.parse(args, Config()) match {
Expand All @@ -247,7 +253,8 @@ case class Config(
justTopLevel: Boolean = false,
dotTimeOut: Int = 7,
useRanking: Boolean = false,
rankDir: String = "LR"
rankDir: String = "LR",
showPrintfs: Boolean = false
) {
def toAnnotations: Seq[Annotation] = {
val dir = {
Expand All @@ -269,7 +276,8 @@ case class Config(
RankDirAnnotation(rankDir)
) ++
(if(startModuleName.nonEmpty) Seq(StartModule(startModuleName)) else Seq.empty) ++
(if(useRanking) Seq(UseRankAnnotation) else Seq.empty)
(if(useRanking) Seq(UseRankAnnotation) else Seq.empty) ++
(if(showPrintfs) Seq(ShowPrintfsAnnotation) else Seq.empty)
}
}

Expand Down
48 changes: 48 additions & 0 deletions src/main/scala/dotvisualizer/dotnodes/PrintfNode.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// See LICENSE for license details.

package dotvisualizer.dotnodes

import firrtl.WRef
import firrtl.ir.Print

import scala.collection.mutable

case class PrintfNode(name: String, formatString: String, parentOpt: Option[DotNode]) extends DotNode {

val text = new mutable.StringBuilder()

override def absoluteName: String = "struct_" + super.absoluteName

text.append(
s"""
|$absoluteName [shape="plaintext" label=<
|<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4" BGCOLOR="#EA3076">
| <TR>
| <TD>printf("$formatString") </TD>
| </TR>
""".stripMargin)

def addArgument(displayName: String, connectTarget: String, connect: String): PrintfArgument = {
val port = PrintfArgument(displayName, connect, connectTarget)
text.append(s" ${port.render}")
port
}

def finish() {
text.append(
"""
|</TABLE>>];
""".stripMargin)
}

def render: String = text.toString()
}

case class PrintfArgument(name: String, override val absoluteName: String, connectTarget: String) extends DotNode {
val parentOpt : Option[DotNode] = None // doesn't need to know parent
def render: String = {
s"""
|<TR><TD PORT="$name">$name</TD></TR>
""".stripMargin
}
}
32 changes: 30 additions & 2 deletions src/main/scala/dotvisualizer/transforms/MakeOneDiagram.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class MakeOneDiagram extends Transform {

val rankDir = state.annotations.collectFirst { case RankDirAnnotation(dir) => dir}.getOrElse("LR")

val showPrintfs = state.annotations.collectFirst { case ShowPrintfsAnnotation => ShowPrintfsAnnotation}.isDefined

val printFileName = s"$targetDir$startModuleName.dot"
println(s"creating dot file $printFileName")
val printFile = new PrintWriter(new java.io.File(printFileName))
Expand Down Expand Up @@ -106,10 +108,14 @@ class MakeOneDiagram extends Transform {
s"${moduleNode.absoluteName}_$name".replaceAll("""\.""", "_")
}

def reducedLongLiteral(s: String): String = {
if(s.length > 32) { s.take(16) + "..." + s.takeRight(16) } else { s }
}

def getLiteralValue(expression: Expression): Option[String] = {
expression match {
case UIntLiteral(x, _) => Some(x.toString)
case SIntLiteral(x, _) => Some(x.toString)
case UIntLiteral(x, _) => Some(reducedLongLiteral(x.toString))
case SIntLiteral(x, _) => Some(reducedLongLiteral(x.toString))
case _ => None
}
}
Expand Down Expand Up @@ -275,6 +281,26 @@ class MakeOneDiagram extends Transform {
moduleNode += memNode
}

def processPrintf(printf: Print): Unit = {
val nodeName = s"printf_${printf.hashCode().abs}"
val printfNode = PrintfNode(nodeName, printf.string.serialize, Some(moduleNode))

printf.args.foreach { arg =>
val displayName = arg.serialize
val connectTarget = s"${printfNode.absoluteName}:$displayName"
val processedARg = processExpression(arg)

val port = printfNode.addArgument(displayName, connectTarget, processedARg)
nameToNode(connectTarget) = port

moduleNode.connect(connectTarget, processedARg)
}
printfNode.finish()


moduleNode += printfNode
}

def getConnectInfo(expression: Expression): String = {
val (fName, dotName) = expression match {
case WRef(name, _, _, _) => (getFirrtlName(name), expand(name))
Expand Down Expand Up @@ -349,6 +375,8 @@ class MakeOneDiagram extends Transform {
val regNode = RegisterNode(reg.name, Some(moduleNode))
nameToNode(getFirrtlName(reg.name)) = regNode
moduleNode += regNode
case printf: Print if showPrintfs =>
processPrintf(printf)
case memory: DefMemory if scope.doComponents() =>
processMemory(memory)
case _ =>
Expand Down
50 changes: 50 additions & 0 deletions src/test/scala/dotvisualizer/PrintfSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// See README.md for license details.

package dotvisualizer

import org.scalatest.{FreeSpec, Matchers}
import chisel3._
import firrtl.FileUtils

class HasPrintf extends MultiIOModule {
val in = IO(Input(Bool()))
val out = IO(Output(Bool()))
out := in
printf("in %d, out %d\n", in, out)
}
class PrintfSpec extends FreeSpec with Matchers {

"printfs can now be rendered" - {
val dirName = "test_run_dir/has_printf_on/"

def makeDotFile(showPrintfs: Boolean): String = {
val circuit = chisel3.Driver.elaborate(() => new HasPrintf)
val firrtl = chisel3.Driver.emit(circuit)
val config = Config(
targetDir = dirName,
firrtlSource = firrtl,
rankDir = "TB",
useRanking = true,
showPrintfs = showPrintfs,
openProgram = ""
)
FirrtlDiagrammer.run(config)

FileUtils.getText(s"${dirName}HasPrintf.dot")
}

"showPrintfs=true will render printfs in dot file" in {
val dotText = makeDotFile(showPrintfs = true)

dotText.contains("struct_cluster_HasPrintf_printf_1") should be(true)
dotText.contains("""printf("in %d, out %d\n")""") should be(true)
}

"default behavior will not render printfs in dot file" in {
val dotText = makeDotFile(showPrintfs = false)

dotText.contains("struct_cluster_HasPrintf_printf_1") should be(false)
dotText.contains("""printf("in %d, out %d\n")""") should be(false)
}
}
}

0 comments on commit 6c1b995

Please sign in to comment.