Skip to content

Commit 1d125c7

Browse files
committed
Add submodule support
It works only if only a single instance of each module is created. It does not work with multiple instances of the same module.
1 parent 52a3d68 commit 1d125c7

File tree

12 files changed

+408
-22
lines changed

12 files changed

+408
-22
lines changed

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
val chiselVersion = "6.1.0-tywaves-SNAPSHOT"
1+
val chiselVersion = "6.1.0-tywaves-SNAPSHOT" // Local version of chisel
22
val scalatestVersion = "3.2.16"
33
val circeVersion = "0.14.6"
44

src/main/scala/chisel3/tywaves/circuitparser/ChiselIRParser.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,13 @@ class ChiselIRParser
184184
case _: chiselIR.Printf => logger.debug("ChiselIRParser: Parsing Printf. Skip."); None
185185
case _: chiselIR.AltBegin => logger.debug("ChiselIRParser: Parsing AltBegin. Skip."); None
186186
case _: chiselIR.OtherwiseEnd => logger.debug("ChiselIRParser: Parsing OtherwiseEnd. Skip."); None
187+
case chiselIR.DefInstance(sourceInfo, module, _ports) => {
188+
val elId = this.createId(sourceInfo, Some(module.name))
189+
// _ports.foreach(parsePort(module.name, _, module.name))
190+
modules.put(elId, (Name(module.name, scope, parentModule), null)) // Add the module and its name
191+
logger.error(s"DefInstance not implemented: $module. Skip.")
192+
None
193+
}
187194
case a =>
188195
logger.error(s"Match case not covered: $a")
189196
None

src/main/scala/chisel3/tywaves/circuitparser/FirrtlIRParser.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,9 @@ class FirrtlIRParser
175175
tpe,
176176
parentModule,
177177
)
178-
178+
case DefInstance(info, name, module, tpe) => {
179+
logger.error(s"DefInstance: name: $name, module: $module, tpe: $tpe. Skip.")
180+
}
179181
case _: Connect => logger.debug("FirrtlIR parser: Parsing Connect. Skip.")
180182
case _: DefNode => logger.debug("FirrtlIR parser: Parsing DefNode. Skip.")
181183
case _: Conditionally => logger.debug("FirrtlIR parser: Parsing Conditionally. Skip.")

src/main/scala/tywaves/circuitmapper/MapChiselToVcd.scala

Lines changed: 82 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,16 @@ class MapChiselToVcd[T <: RawModule](generateModule: () => T, private val workin
5252
chiselIRParser.parseCircuit(circuitChiselIR)
5353

5454
// Step 4. Parse the debug information
55-
private val gDebugIRParser = new DebugIRParser(workingDir, TypedConverter.getDebugIRFile(gOpt = true))
55+
private val topModuleName = circuitChiselIR.name
56+
private val gDebugIRParser = new DebugIRParser(workingDir, TypedConverter.getDebugIRFile(gOpt = true, topModuleName))
57+
logger.error(s"HGDD file used: ${TypedConverter.getDebugIRFile(gOpt = true, topModuleName)}")
5658
// val debugIRParser =
5759
// new DebugIRParser(workingDir, TypedConverter.getDebugIRFile(gOpt = false)) // TODO: check if this is needed or not
5860

5961
gDebugIRParser.parse()
6062
// debugIRParser.parse()
63+
mapCircuits()
64+
dumpLog()
6165

6266
/** Print debug information */
6367
def printDebug(): Unit = {
@@ -187,21 +191,25 @@ class MapChiselToVcd[T <: RawModule](generateModule: () => T, private val workin
187191

188192
// Each element is associated to the 3 IRs
189193
// The goal here is to create a Tywavestate
190-
val (childVariables, childScopes) = groupIrPerElement.flatMap {
194+
val (childVariables, _) = groupIrPerElement.flatMap {
191195
case (elId, irs) =>
192196
logger.info(s"Element: $elId")
193197
// Iterate over the IRs
194198
irs.flatMap {
195199
case (ir, Some(value)) =>
196-
Some((findChildVariable(elId, value, ir), findChildScopes(value)))
200+
Some((
201+
findChildVariable(elId, value, ir, circuitChiselIR.name),
202+
Seq.empty,
203+
))
197204
case (ir, None) =>
198205
logger.debug(s"IR without match: $ir", None)
199206
None
200207
}.filter { case (variableOpt, _) => variableOpt.isDefined }
201208
.map { case (variableOpt, scopes) => (variableOpt.get, scopes) }
202-
}.unzip
203209

204-
val scopes = Seq(tywaves_symbol_table.Scope(name = dutName, childVariables.toSeq, childScopes.flatten.toSeq))
210+
}.unzip
211+
val childScopes = findChildScopes(circuitChiselIR.name, groupIrPerElement)
212+
val scopes = Seq(tywaves_symbol_table.Scope(name = dutName, childVariables.toSeq, childScopes))
205213
// Finalize the scopes -> mergeScopes(scope) is not needed anymore
206214
val finalScopes = cleanFromFlattenedSignals(scopes)
207215

@@ -294,16 +302,23 @@ class MapChiselToVcd[T <: RawModule](generateModule: () => T, private val workin
294302
}
295303

296304
/** Find the child variables of a given tuple for a given representation */
297-
private def findChildVariable[Tuple](elId: ElId, tuple: Tuple, ir: String): Option[tywaves_symbol_table.Variable] =
305+
private def findChildVariable[Tuple](
306+
elId: ElId,
307+
tuple: Tuple,
308+
ir: String,
309+
moduleScope: String,
310+
): Option[tywaves_symbol_table.Variable] =
298311
if (ir == "debugIR")
299312
tuple match {
300313
case (
301-
Name(name, scope, _),
314+
Name(name, scope, tywaveScope),
302315
Direction(dir),
303316
HardwareType(hardwareType, size),
304317
Type(_),
305318
VerilogSignals(verilogSignals),
306319
) =>
320+
if (moduleScope != tywaveScope)
321+
return None
307322
// Get the list of children of this variable
308323
val listChildren = gDebugIRParser.signals.filter {
309324
case (_: ElId, (Name(_, childScope, _), Direction(_), HardwareType(_, _), Type(_), VerilogSignals(_))) =>
@@ -319,7 +334,7 @@ class MapChiselToVcd[T <: RawModule](generateModule: () => T, private val workin
319334
// TODO: Understand how to handle other types
320335
tywaves_symbol_table.realtype.Bundle(
321336
fields = listChildren.flatMap { child =>
322-
findChildVariable(child._1, child._2, ir)
337+
findChildVariable(child._1, child._2, ir, moduleScope)
323338
}.toSeq,
324339
vcdName = Some(name),
325340
)
@@ -342,7 +357,63 @@ class MapChiselToVcd[T <: RawModule](generateModule: () => T, private val workin
342357
None
343358
}
344359

345-
private def findChildScopes[Tuple](value: Tuple): Seq[tywaves_symbol_table.Scope] =
346-
// TODO: implement it to support submodules
347-
Seq.empty
360+
private def findChildScopes(
361+
parentScope: String,
362+
groupIrPerElement: Map[ElId, Seq[(String, Option[?])]],
363+
): Seq[tywaves_symbol_table.Scope] = {
364+
365+
var childScopes: Seq[tywaves_symbol_table.Scope] = Seq.empty
366+
367+
println(s"findChildScopes of: $parentScope")
368+
gDebugIRParser.modules.foreach(println(_))
369+
370+
val childScopeNames = gDebugIRParser.modules.filter {
371+
case (_, Name(_, _, tywaveScope)) =>
372+
tywaveScope == parentScope
373+
}.map { case (_, Name(name, scope, _)) => (name, scope) }.toSet
374+
375+
childScopeNames.foreach(println)
376+
377+
childScopeNames.foreach { x =>
378+
val (childScopeName, instanceName) = x
379+
val (childVariables, _) = groupIrPerElement.flatMap {
380+
case (elId, irs) =>
381+
logger.info(s"Element: $elId")
382+
// Iterate over the IRs
383+
irs.flatMap {
384+
case (ir, Some(value)) =>
385+
Some((
386+
findChildVariable(elId, value, ir, childScopeName),
387+
Seq.empty,
388+
))
389+
case (ir, None) =>
390+
logger.debug(s"IR without match: $ir", None)
391+
None
392+
}.filter { case (variableOpt, _) => variableOpt.isDefined }
393+
.map { case (variableOpt, scopes) => (variableOpt.get, scopes) }
394+
395+
}.unzip
396+
childScopes :+= tywaves_symbol_table.Scope(instanceName, childVariables.toSeq, findChildScopes(childScopeName, groupIrPerElement))
397+
}
398+
childScopes
399+
// val (childVariables, childScopes) = groupIrPerElement.flatMap {
400+
// case (elId, irs) =>
401+
// logger.info(s"Element: $elId")
402+
// // Iterate over the IRs
403+
// irs.flatMap {
404+
// case (ir, Some(value)) =>
405+
// Some((findChildVariable(elId, value, ir, childScope), findChildScopes(circuitChiselIR.name)))
406+
// case (ir, None) =>
407+
// logger.debug(s"IR without match: $ir", None)
408+
// None
409+
// }.filter { case (variableOpt, _) => variableOpt.isDefined }
410+
// .map { case (variableOpt, scopes) => (variableOpt.get, scopes) }
411+
//
412+
// }.unzip
413+
// Find all the child scopes of this scope
414+
415+
// Check if the scope is
416+
// Seq.empty
417+
}
418+
// TODO: implement it to support submodules
348419
}

src/main/scala/tywaves/circuitmapper/TypedConverter.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,13 @@ private object TypedConverter {
5656
}
5757

5858
/** Get the name of the debug file */
59-
def getDebugIRFile(gOpt: Boolean): String = {
59+
def getDebugIRFile(gOpt: Boolean, topModuleName: String): String = {
6060

6161
def getFile(_workingDir: String): String = {
6262
val workingDir = new java.io.File(_workingDir)
6363
// Open the HGLDD file and extract the information
6464
if (workingDir.exists() && workingDir.isDirectory) {
65-
workingDir.listFiles().filter(_.getName.endsWith(debugFileExt)).head.getAbsolutePath
65+
workingDir.listFiles().filter(_.getName == topModuleName + debugFileExt).head.getAbsolutePath
6666
} else
6767
throw new Exception(s"WorkingDir: $workingDir does not exist or is not a directory.")
6868
}
@@ -81,6 +81,6 @@ object GenerateHgldd {
8181
def apply[T <: RawModule](generateModule: () => T, workingDir: String = "workingDir"): String = {
8282
val mapChiselToVcd = new MapChiselToVcd(generateModule, workingDir)("TOP", "TbName", "DutName")
8383
mapChiselToVcd.dumpLog()
84-
TypedConverter.getDebugIRFile(gOpt = true)
84+
TypedConverter.getDebugIRFile(gOpt = true, generateModule.toString())
8585
}
8686
}

src/main/scala/tywaves/hglddparser/DebugIRParser.scala

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,30 @@ class DebugIRParser(val workingDir: String, ddFilePath: String) {
9090
}
9191

9292
/** Parse an object from the HGLDD representation */
93-
private def parseObject(fileInfo: Seq[String], hglddObject: HglddObject): Unit = {
93+
private def parseObject(
94+
fileInfo: Seq[String],
95+
hglddObject: HglddObject,
96+
thisChildParentName: Option[String] = None,
97+
thisChildName: Option[String] = None,
98+
): Unit = {
9499

95100
val scope =
96-
hglddObject.obj_name.lastIndexOf("_") match { case -1 => "root"; case i => hglddObject.obj_name.substring(0, i) }
101+
hglddObject.obj_name.lastIndexOf("_") match {
102+
case -1 => thisChildName.getOrElse("root"); case i => hglddObject.obj_name.substring(0, i)
103+
}
97104
// Drop the scope from the object name
98105
val obj_name =
99-
scope match { case "root" => hglddObject.obj_name; case _ => hglddObject.obj_name.substring(scope.length + 1) }
106+
if (scope == thisChildName.getOrElse("root"))
107+
hglddObject.obj_name
108+
else
109+
hglddObject.obj_name.substring(scope.length + 1)
110+
100111
val elId =
101112
createId(fileInfo, hglddObject.hgl_loc, obj_name)
102113
val parentModule =
103-
hglddObject.obj_name.lastIndexOf("_") match { case -1 => "root"; case i => hglddObject.obj_name.substring(0, i) }
114+
hglddObject.obj_name.lastIndexOf("_") match {
115+
case -1 => thisChildParentName.getOrElse("root"); case i => hglddObject.obj_name.substring(0, i)
116+
}
104117
// Parse the kind of the object
105118
hglddObject.kind match {
106119
case s @ "struct" =>
@@ -114,6 +127,31 @@ class DebugIRParser(val workingDir: String, ddFilePath: String) {
114127
???
115128
}
116129

130+
hglddObject.children match {
131+
case Some(children) =>
132+
children.foreach(parseChild(_, hglddObject.obj_name))
133+
case None => ()
134+
}
135+
136+
}
137+
138+
def parseChild(child: Child, parentModuleName: String): Unit = {
139+
val childPath = ddFilePath.substring(0, ddFilePath.lastIndexOf("/")) + "/" + child.module_name + ".dd"
140+
val hgldd = parseFile(childPath)
141+
val (fileInfo, hdlFileActualIndex) = (hgldd.HGLDD.file_info, hgldd.HGLDD.hdl_file_index - 1)
142+
143+
hgldd.objects.foreach(
144+
parseObject(
145+
fileInfo.map(f =>
146+
f.replaceAll("\\.\\./", "")
147+
), // Use the same of the other parsers (ChiselIR and FIRRTL IR) TODO: needs better version
148+
_,
149+
thisChildParentName = Some(parentModuleName),
150+
thisChildName = Some(child.name),
151+
)
152+
)
153+
154+
// ???
117155
}
118156

119157
/**

src/main/scala/tywaves/hglddparser/package.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ package object hglddparser {
4545
port_vars: Seq[PortVar],
4646
module_name: Option[String],
4747
hdl_loc: Option[HdlLocation],
48-
children: Option[Seq[Children]],
48+
children: Option[Seq[Child]],
4949
)
5050

5151
case class HglLocation(
@@ -92,6 +92,7 @@ package object hglddparser {
9292
opcode: Option[String],
9393
operands: Option[Seq[Value]],
9494
)
95-
case class Children()
95+
96+
case class Child(name: String, obj_name: String, module_name: String, hgl_loc: HglLocation, hdl_loc: HdlLocation)
9697

9798
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package hierarchicalmodules
2+
3+
import chisel3._
4+
import chisel3.util.Counter
5+
6+
class Blink(period: Int) extends Module {
7+
assert(period > 0, "limit must be greater than 0")
8+
val io = IO(new Bundle {
9+
val enable: Bool = Input(Bool())
10+
val led : Bool = Output(Bool())
11+
})
12+
13+
val cnt: Counter = Counter(period)
14+
val ledReg: Bool = RegInit(false.B)
15+
16+
when(io.enable) {
17+
when(cnt.inc() && cnt.value === (period - 1).U) {
18+
ledReg := ~ledReg
19+
}
20+
}
21+
22+
io.led := ledReg
23+
}
24+
25+
object MainBlink extends App {
26+
import circt.stage.ChiselStage
27+
28+
println(
29+
ChiselStage.emitSystemVerilog(
30+
new Blink(4),
31+
firtoolOpts = Array(
32+
"-O=debug",
33+
"-g",
34+
"--disable-all-randomization",
35+
"--strip-debug-info",
36+
),
37+
)
38+
)
39+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package hierarchicalmodules
2+
3+
import chisel3.fromBooleanToLiteral
4+
import org.scalatest.funspec.AnyFunSpec
5+
import org.scalatest.matchers.must.Matchers
6+
7+
class BlinkTest extends AnyFunSpec with Matchers {
8+
9+
private def blinkTb(dut: => Blink): Unit = {
10+
import chisel3.simulator.PeekPokeAPI._
11+
dut.reset.poke(true.B)
12+
dut.io.enable.poke(true.B)
13+
dut.clock.step(5)
14+
dut.reset.poke(false.B)
15+
16+
dut.io.led.expect(false.B)
17+
dut.clock.step() // 1
18+
dut.io.led.expect(false.B)
19+
dut.clock.step() // 2
20+
dut.io.led.expect(false.B)
21+
dut.clock.step() // 3
22+
dut.io.led.expect(false.B)
23+
dut.clock.step() // 4
24+
dut.io.led.expect(true.B)
25+
dut.clock.step() // 5
26+
dut.io.led.expect(true.B)
27+
dut.clock.step() // 6
28+
dut.io.led.expect(true.B)
29+
dut.clock.step()
30+
dut.clock.step(30)
31+
}
32+
33+
describe("Blink with parametric simulator") {
34+
import tywaves.simulator.ParametricSimulator._
35+
import tywaves.simulator.simulatorSettings._
36+
37+
it("should work") {
38+
simulate(
39+
new Blink(4),
40+
Seq(VcdTrace, WithFirtoolArgs(Seq("-O=debug", "-g"))),
41+
simName = "blink_with_parametric_sim_should_work",
42+
) { dut =>
43+
blinkTb(dut)
44+
}
45+
}
46+
} // end of describe("Blink with parametric simulator")
47+
48+
describe("Blink with tywaves simulator") {
49+
import tywaves.simulator.TywavesSimulator._
50+
import tywaves.simulator.simulatorSettings._
51+
52+
it("should work") {
53+
simulate(
54+
new Blink(4),
55+
Seq(VcdTrace, WithTywavesWaveforms(true), SaveWorkdirFile("workdir")),
56+
simName = "blink_with_tywaves_sim_should_work",
57+
) { dut =>
58+
blinkTb(dut)
59+
}
60+
}
61+
} // end of describe("Blink with tywaves simulator")
62+
}

0 commit comments

Comments
 (0)