@@ -19,14 +19,25 @@ import $ivy.`edu.berkeley.cs::chiseltest:0.6.+`
1919import $ivy .`edu.berkeley.cs::dsptools:1.5.+`
2020import $ivy .`org.scalanlp::breeze:1.0`
2121import $ivy .`edu.berkeley.cs::rocket-dsptools:1.2.0`
22- import $ivy .`edu.berkeley.cs::firrtl-diagrammer:1.6.+`
22+
23+ // firrtl-diagrammer removed due to json4s compatibility issues with Chisel 3.6
24+ // Visualization functions below provide alternative output
2325
2426import $ivy .`org.scalatest::scalatest:3.2.2`
2527
2628// Convenience function to invoke Chisel and grab emitted Verilog.
29+ // Note: emitVerilog has json4s compatibility issues, using FIRRTL as fallback
2730def getVerilog (dut : => chisel3.Module ): String = {
2831 import chisel3 .stage .ChiselStage
29- (new ChiselStage ).emitVerilog(dut)
32+ try {
33+ (new ChiselStage ).emitVerilog(dut)
34+ } catch {
35+ case e : NoSuchMethodError if e.getMessage.contains(" json4s" ) =>
36+ println(" Warning: Verilog generation has json4s compatibility issues, using FIRRTL" )
37+ (new ChiselStage ).emitChirrtl(dut)
38+ case e : Exception =>
39+ throw e
40+ }
3041}
3142
3243// Convenience function to invoke Chisel and grab emitted FIRRTL.
@@ -92,76 +103,143 @@ def stringifyAST(firrtlAST: firrtl.ir.Circuit): String = {
92103 buf.toString
93104}
94105
95- // Returns path to module viz and hierarchy viz
96- def generateVisualizations (gen : () => chisel3.RawModule ): (String , String ) = {
97- import dotvisualizer ._
98- import dotvisualizer .transforms ._
99-
100- import java .io ._
101- import firrtl ._
102- import firrtl .annotations ._
103-
104- import almond .interpreter .api .DisplayData
105- import almond .api .helpers .Display
106-
106+ // Visualization functions - graphical SVG generation using graphviz
107+ def visualize (gen : () => chisel3.RawModule ): Unit = {
107108 import chisel3 ._
108- import chisel3 .stage ._
109- import firrtl .ir .Module
109+ import chisel3 .stage .ChiselGeneratorAnnotation
110+ import chisel3 .stage .phases .{Elaborate , Convert }
111+ import firrtl .stage .FirrtlCircuitAnnotation
110112 import sys .process ._
113+ import java .io .{File , PrintWriter }
114+ import almond .interpreter .api .DisplayData
115+ import almond .api .helpers .Display
111116
112- val sourceFirrtl = scala.Console .withOut(new PrintStream (new ByteArrayOutputStream ())) {
113- (new ChiselStage ).emitChirrtl(gen())
117+ // Step 1: Elaborate and convert to FIRRTL
118+ val elaboratePhase = new Elaborate
119+ val elaborated = elaboratePhase.transform(Seq (ChiselGeneratorAnnotation (gen)))
120+
121+ val convertPhase = new Convert
122+ val converted = convertPhase.transform(elaborated)
123+
124+ val firrtlCircuit = converted.collectFirst {
125+ case FirrtlCircuitAnnotation (cir) => cir
126+ }.get
127+
128+ val firrtlString = firrtlCircuit.serialize
129+
130+ // Parse FIRRTL to extract structure
131+ val lines = firrtlString.split(" \n " )
132+ val moduleName = lines.find(_.trim.startsWith(" module " )).map(_.trim.split(" " )(1 ).replace(" :" , " " )).getOrElse(" Module" )
133+ val inputs = lines.filter(_.trim.startsWith(" input " )).map(l => l.trim.split(" " )(1 ).split(" :" )(0 ))
134+ val outputs = lines.filter(_.trim.startsWith(" output " )).map(l => l.trim.split(" " )(1 ).split(" :" )(0 ))
135+ val regs = lines.filter(_.trim.startsWith(" reg " )).map(l => l.trim.split(" " )(1 ).split(" :" )(0 ))
136+ val wires = lines.filter(_.trim.startsWith(" wire " )).map(l => l.trim.split(" " )(1 ).split(" :" )(0 ))
137+
138+ // Generate GraphViz DOT
139+ val dot = new StringBuilder
140+ dot ++= " digraph circuit {\n "
141+ dot ++= " rankdir=LR;\n "
142+ dot ++= " node [shape=box, style=rounded];\n\n "
143+
144+ // Input nodes
145+ dot ++= " subgraph cluster_inputs {\n "
146+ dot ++= " label=\" Inputs\" ;\n "
147+ dot ++= " style=filled; color=lightblue;\n "
148+ inputs.foreach(i => dot ++= s " $i [shape=circle, fillcolor=lightgreen, style=filled]; \n " )
149+ dot ++= " }\n\n "
150+
151+ // Output nodes
152+ dot ++= " subgraph cluster_outputs {\n "
153+ dot ++= " label=\" Outputs\" ;\n "
154+ dot ++= " style=filled; color=lightblue;\n "
155+ outputs.foreach(o => dot ++= s " $o [shape=doublecircle, fillcolor=lightcoral, style=filled]; \n " )
156+ dot ++= " }\n\n "
157+
158+ // Register nodes
159+ if (regs.nonEmpty) {
160+ dot ++= " subgraph cluster_regs {\n "
161+ dot ++= " label=\" Registers\" ;\n "
162+ dot ++= " style=filled; color=lightyellow;\n "
163+ regs.foreach { r =>
164+ dot ++= s " $r [shape=box, fillcolor=yellow, style=filled]; \n "
165+ }
166+ dot ++= " }\n\n "
114167 }
115- val ast = Parser .parse(sourceFirrtl)
116-
117- val uniqueTopName = ast.main + ast.hashCode().toHexString
118168
119- val targetDir = s " diagrams/ $uniqueTopName/ "
169+ // Parse connections from FIRRTL
170+ lines.filter(l => l.contains(" <=" ) && ! l.trim.startsWith(" reset" )).foreach { line =>
171+ val parts = line.trim.split(" <=" ).map(_.trim)
172+ if (parts.length == 2 ) {
173+ val target = parts(0 ).split(" \\ ." )(0 ).split(" \\ (" )(0 )
174+ val source = parts(1 ).split(" \\ ." )(0 ).split(" \\ (" )(0 ).split(" " )(0 )
175+ if (! source.startsWith(" UInt" ) && ! source.contains(" \" " )) {
176+ dot ++= s " $source -> $target; \n "
177+ }
178+ }
179+ }
120180
121- val cmdRegex = " cmd[0-9]+([A-Za-z]+.*)" .r
122- val readableTop = ast.main match {
123- case cmdRegex(n) => n
124- case other => other
181+ dot ++= " }\n "
182+
183+ // Write DOT file and generate SVG
184+ val dotFile = File .createTempFile(" circuit" , " .dot" )
185+ val svgFile = File .createTempFile(" circuit" , " .svg" )
186+ val pw = new PrintWriter (dotFile)
187+ pw.write(dot.toString)
188+ pw.close()
189+
190+ // Generate SVG using graphviz
191+ val result = s " dot -Tsvg ${dotFile.getAbsolutePath} -o ${svgFile.getAbsolutePath}" .!
192+
193+ if (result == 0 && svgFile.exists()) {
194+ val svgContent = scala.io.Source .fromFile(svgFile).mkString
195+ Display .html(svgContent)
196+ } else {
197+ println(" === Module FIRRTL (Intermediate Representation) ===" )
198+ println(firrtlString)
199+ println(" \n Note: Graphviz not available. Install with: apt-get install graphviz" )
125200 }
126- val newTop = readableTop
127201
128- // Console hack prevents unnecessary chatter appearing in cell
129- scala.Console .withOut(new PrintStream (new ByteArrayOutputStream ())) {
130- val sourceFirrtl = (new ChiselStage ).emitChirrtl(gen())
202+ // Cleanup
203+ dotFile.delete()
204+ svgFile.delete()
205+ }
131206
132- val newModules : Seq [firrtl.ir.DefModule ] = ast.modules.map {
133- case m : Module if m.name == ast.main => m.copy(name = newTop)
134- case other => other
135- }
136- val newAst = ast.copy(main = newTop, modules = newModules)
207+ def visualizeHierarchy (gen : () => chisel3.RawModule ): Unit = {
208+ import chisel3 ._
209+ import chisel3 .stage .ChiselGeneratorAnnotation
210+ import chisel3 .stage .phases .{Elaborate , Convert }
211+ import chisel3 .stage .ChiselCircuitAnnotation
212+ import firrtl .stage .FirrtlCircuitAnnotation
137213
138- val controlAnnotations : Seq [Annotation ] = Seq (
139- firrtl.stage.FirrtlSourceAnnotation (sourceFirrtl),
140- firrtl.options.TargetDirAnnotation (targetDir),
141- dotvisualizer.stage.OpenCommandAnnotation (" " )
142- )
214+ println(" === Module Hierarchy ===" )
143215
144- (new dotvisualizer.stage.DiagrammerStage ).execute(Array .empty, controlAnnotations)
145- }
146- val moduleView = s """ $targetDir/ $newTop.dot.svg """
147- val instanceView = s """ $targetDir/ ${newTop}_hierarchy.dot.svg """
216+ // Elaborate and convert
217+ val elaboratePhase = new Elaborate
218+ val elaborated = elaboratePhase.transform(Seq (ChiselGeneratorAnnotation (gen)))
148219
149- val svgModuleText = FileUtils .getText(moduleView)
150- val svgInstanceText = FileUtils .getText(instanceView )
220+ val convertPhase = new Convert
221+ val converted = convertPhase.transform(elaborated )
151222
152- val x = s """ <div width="100%" height="100%" overflow="scroll"> $svgModuleText</div> """
153- val y = s """ <div> width="100%" height="100%" overflow="scroll"> $svgInstanceText</div> """
223+ val firrtlCircuit = converted.collectFirst {
224+ case FirrtlCircuitAnnotation (cir) => cir
225+ }.get
154226
155- (x, y)
156- }
227+ val firrtlString = firrtlCircuit.serialize
157228
158- def visualize (gen : () => chisel3.RawModule ): Unit = {
159- val (moduleView, instanceView) = generateVisualizations(gen)
160- html(moduleView)
161- }
229+ // Print just the module hierarchy
230+ val hierarchyLines = firrtlString.split(" \n " ).filter(line =>
231+ line.trim.startsWith(" module " ) ||
232+ line.trim.startsWith(" inst " ) ||
233+ line.trim.matches(" ^\\ s+inst .*" )
234+ )
162235
163- def visualizeHierarchy (gen : () => chisel3.RawModule ): Unit = {
164- val (moduleView, instanceView) = generateVisualizations(gen)
165- html(instanceView)
236+ if (hierarchyLines.nonEmpty) {
237+ hierarchyLines.foreach(println)
238+ } else {
239+ println(" (No submodule instances found)" )
240+ }
241+
242+ println(" \n === Full FIRRTL ===" )
243+ println(firrtlString)
166244}
167245
0 commit comments