diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f02872c..c0bf39d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,6 @@ name: Continuous Integration on: - pull_request: push: env: @@ -9,7 +8,7 @@ env: jobs: run: - name: test + name: Compile strategy: matrix: java-version: [17] @@ -27,5 +26,10 @@ jobs: java-version: ${{ matrix.java-version }} cache: sbt - - name: Run the unit tests + # cache sbt dependencies + - uses: coursier/cache-action@v6 + + - uses: sbt/setup-sbt@v1 + + - name: unit test run: sbt test diff --git a/README.md b/README.md index d5a7854..4120f93 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,24 @@ # dependentChisel -This is the impl for Master thesis : Dependent Chisel: Statically-checked hardware designs based on Chisel and Scala 3, by Yuchen du +This is an ongoing research project to bring dependent type features to hardware design in Chisel-like syntax, as an embedded DSL(eDSL) in Scala 3. + +It originally started as Master thesis "Dependent Chisel: Statically-checked hardware designs based on Chisel and Scala 3", by Yuchen du in 2023 https://github.com/doofin/dependentChisel/blob/master/Msc_Thesis_yuchen.pdf -It uses partial dependent types in Scala 3 to provide early error message for Chisel, a hardware description language embedded in Scala. -This allows you to identify bitwidth mismatch at compile time where IDE can show errors instantly in the editor. +## Design +The main philosophy is to make it clean and simple, and avoid complex language features. The core intermediate representation is an algebraic AST, which is easy to analyze and transform. + +**algebraic AST** + +The layer of DSL translation: + +Chisel-like syntax -> list of commands as stack data structure -> Algebraic AST (FIRRTL like IR in Scala ADT) + +when we have the algebraic AST, it's convenient to do various analysis, transformation and finally code generation to FIRRTL. This algebraic AST is similar to FIRRTL IR in Chisel, but Chisel seems to discourage direct manipulation of FIRRTL IR AST, while we embrace it. + +**dependent types** + +It uses partial dependent types in Scala 3 to provide early error message, which can be directly shown in IDE when writing code, allowing you to catch bitwidth mismatch at compile time. ## examples examples like adder, etc. can be found in src/test/scala/dependentChisel/ @@ -55,13 +69,12 @@ Instantiate those modules : ## Interop with chisel -The current implementation is based on Chisel 3.5.1, which is used internally. There's no direct interop with the original Chisel, but you can probably use the generated FIRRTL for that. +The current implementation is based on Chisel 3.5.1, which is used internally. There's no direct interop with the original Chisel, but you can probably use the generated FIRRTL for interop. -Although scala 3 can invoke scala 2.13 libraries,chisel uses scala 2 macros different from scala 3 ,making it partially incompatible. +Although scala 3 can invoke scala 2.13 libraries,chisel uses scala 2 macros which is different from scala 3 ,making it incompatible. To fix the mismatch, there are several possible ways: - - Rewrite all macros and make everything compatible. - Rewrite some macros and extend some base class. - Write a new frontend and emit FIRRTL. @@ -78,13 +91,16 @@ many tests under src/test can be run by ### IDE support +recommended IDEs: [Metals](https://scalameta.org/metals/) with vscode -[IntelliJ](https://blog.jetbrains.com/scala/) - ## theories and related work related work : https://github.com/doofin/dependentChisel/blob/master/resources.md +similar projects: +- [zaozi] : https://github.com/sequencer/zaozi + +and more are listed in the thesis pdf. ## misc @@ -92,4 +108,4 @@ with git ls-files | grep '\.scala$' | xargs wc -l -chisel has 60927 total loc +chisel has 60927 total loc, while dependentChisel only has 3765 total loc, so it's a good idea to understand dependentChisel before diving into chisel codebase. diff --git a/src/main/scala/dependentChisel/algo/Tree.scala b/src/main/scala/dependentChisel/algo/Tree.scala index 9614287..ca391c4 100644 --- a/src/main/scala/dependentChisel/algo/Tree.scala +++ b/src/main/scala/dependentChisel/algo/Tree.scala @@ -5,6 +5,6 @@ import scala.collection.mutable.ArrayBuffer object Tree { case class TreeNode[t]( val value: t, - val cld: ArrayBuffer[TreeNode[t]] = ArrayBuffer[TreeNode[t]]() + val children: ArrayBuffer[TreeNode[t]] = ArrayBuffer[TreeNode[t]]() ) } diff --git a/src/main/scala/dependentChisel/algo/seqCmd2tree.scala b/src/main/scala/dependentChisel/algo/stackList2tree.scala similarity index 59% rename from src/main/scala/dependentChisel/algo/seqCmd2tree.scala rename to src/main/scala/dependentChisel/algo/stackList2tree.scala index c43cb34..3d16f0a 100644 --- a/src/main/scala/dependentChisel/algo/seqCmd2tree.scala +++ b/src/main/scala/dependentChisel/algo/stackList2tree.scala @@ -1,23 +1,27 @@ package dependentChisel.algo import scala.util.* +import scala.collection.mutable.Stack import Tree.* import com.doofin.stdScalaCross.* -import dependentChisel.codegen.seqCommands.* +import dependentChisel.codegen.sequentialCommands.* -/** algorithm to convert sequential commands to AST */ -object seqCmd2tree { - type AST = TreeNode[NewInstStmt | FirStmt | Ctrl | VarDecls] +/** algorithm to convert sequential commands to AST in tree structure + */ +object stackList2tree { + type AST = TreeNode[NewInstance | WeakStmt | Ctrl | VarDecls] - /** convert sequential commands to AST. multiple stmt is appended as multiple nodes + /** convert sequential commands to AST. + * + * @param cmdList + * list of sequential commands which implicitly has stack structure + * @return + * AST tree structure where parent node has multiple children nodes */ def list2tree(cmdList: List[Cmds]): AST = { - import scala.collection.mutable.Stack val parents: Stack[AST] = Stack(TreeNode(Ctrl.Top())) // new Stack[AST] - // parents.push(TreeNode(Ctrl.Top())) - // ppc(cmdList) cmdList.foreach { cmd => // dbg(cmd) @@ -27,15 +31,15 @@ object seqCmd2tree { then push new node into parent stack as new top elem*/ val newParNode: AST = TreeNode(ctrl) // new parent node // add this newParNode as child - parents.top.cld += newParNode + parents.top.children += newParNode parents push newParNode case End(ctrl, uid) => // end of block, pop out one parent parents.pop() // for other stmt,just append - case stmt: (FirStmt | NewInstStmt | VarDecls) => + case stmt: (WeakStmt | NewInstance | VarDecls) => val newNd: AST = TreeNode(stmt) - parents.top.cld += newNd + parents.top.children += newNd case _ => } diff --git a/src/main/scala/dependentChisel/algo/treeTraverse.scala b/src/main/scala/dependentChisel/algo/treeTraverse.scala new file mode 100644 index 0000000..b2b959e --- /dev/null +++ b/src/main/scala/dependentChisel/algo/treeTraverse.scala @@ -0,0 +1,46 @@ +package dependentChisel.algo + +import scala.collection.mutable.ArrayBuffer + +/** tree traversal algorithms + */ +object treeTraverse { + + /** imperitive pre-order traversal + * + * @param node + * @param visit + */ + def preOrder[t](visit: t => Unit, node: Tree.TreeNode[t]): Unit = { + visit(node.value) + node.children.foreach(preOrder(visit, _)) + } + + /** filter tree nodes based on predicate + * + * TODO: test this function + * @param predicate + * @param node + * @return + * tree with nodes filtered + */ + def filter[t]( + predicate: t => Boolean, + node: Tree.TreeNode[t] + ): Tree.TreeNode[t] = { + val flag @ (yes, hasChild) = (predicate(node.value), node.children.nonEmpty) + + // if children is empty, it's leaf node, just return itself if satisfies predicate + if hasChild then { + // recursively filter its children + val filteredChildren = node.children + .map(child => filter(predicate, child)) + .filter(child => predicate(child.value) || child.children.nonEmpty) + Tree.TreeNode(node.value, filteredChildren) + } else { + // leaf node, just return itself if satisfies predicate + if yes then node else Tree.TreeNode(node.value, ArrayBuffer()) + } + + } +} diff --git a/src/main/scala/dependentChisel/codegen/compiler.scala b/src/main/scala/dependentChisel/codegen/compiler.scala index 8ee2001..5e81533 100644 --- a/src/main/scala/dependentChisel/codegen/compiler.scala +++ b/src/main/scala/dependentChisel/codegen/compiler.scala @@ -9,9 +9,9 @@ import dependentChisel.typesAndSyntax.statements.* import dependentChisel.global import dependentChisel.typesAndSyntax.chiselModules.* -import dependentChisel.algo.seqCmd2tree.* +import dependentChisel.algo.stackList2tree.* -import seqCommands.* +import sequentialCommands.* import firrtlTypes.* import scala.util.* @@ -39,11 +39,11 @@ object compiler { /** chisel ModLocalInfo to FirrtlModule(IO bundle,AST for the circuit) */ def chiselMod2firrtlCircuits(chiselMod: UserModule, printCmdList: Boolean = false) = { - val modInfo: ModLocalInfo = chiselMod.modLocalInfo + val modInfo: ModuleData = chiselMod.moduleData val allMods: List[UserModule] = chiselMod.globalInfo.modules.toList val typeMap = - allMods.map(_.modLocalInfo.typeMap) reduce (_ ++ _) + allMods.map(_.moduleData.typeMap) reduce (_ ++ _) val mainModuleName = modInfo.className @@ -72,7 +72,7 @@ object compiler { typeMap: mutable.Map[Expr[?], Int], printCmdList: Boolean )(chiselMod: UserModule): FirrtlModule = { - val modInfo: ModLocalInfo = chiselMod.modLocalInfo + val modInfo: ModuleData = chiselMod.moduleData // pp(modInfo.typeMap) val cmdList = modInfo.commands.toList if printCmdList then pp(modInfo.commands.toList) @@ -81,7 +81,7 @@ object compiler { val cmds_ANF: List[Cmds] = expandCmdList(cmds_widthChk) // dbg(cmds_ANF) - val ioNameChanged: List[Cmds] = cmdsTransform(modInfo.thisInstanceName, cmds_ANF) + val ioNameChanged: List[Cmds] = cmdsTransform(modInfo.instanceName, cmds_ANF) // dbg(ioNameChanged) val tree: AST = list2tree(ioNameChanged) FirrtlModule(modInfo, modInfo.io.toList, tree) @@ -103,10 +103,9 @@ object compiler { val circuitStr = tree2firrtlStr(fMod.ast, indent) + skip // println("circuitStr.isEmpty:" + circuitStr.trim().isEmpty()) // pp(circuitStr) - val instName: String = fMod.modInfo.thisInstanceName // name changes for io + val instName: String = fMod.modInfo.instanceName // name changes for io val ioInfoStr = fMod.io.reverse // looks better .map { (x: IOdef) => - val prefix = x.tpe match { case VarType.Input => "flip" case VarType.Output => "" @@ -137,19 +136,17 @@ object compiler { /** can insert more commands */ def expandCmdList(cmdList: List[Cmds]): List[Cmds] = { cmdList flatMap { - case x: FirStmt => + case x: WeakStmt => val fir = stmtToSingleAssign(x) // dbg(fir) fir case orig @ Start(ctrl: Ctrl, uid) => ctrl match { case ctrlIf @ Ctrl.If(bool) => - val anf_stmts: List[FirStmt] = + val anf_stmts: List[WeakStmt] = stmtToSingleAssign(expr2stmtBind(bool)) val anf_res = - anf_stmts :+ orig.copy(ctrl = - ctrlIf.copy(cond = anf_stmts.last.lhs.asTypedUnsafe[1]) - ) + anf_stmts :+ orig.copy(ctrl = ctrlIf.copy(cond = anf_stmts.last.lhs.asTypedUnsafe[1])) // dbg(anf_res) anf_res case _ => List(orig) // bug! will eat "else" @@ -167,13 +164,13 @@ object compiler { case Ctrl.Else() => "else :" case Ctrl.Top() => "" }) - case stmt: FirStmt => indent + stmt2firrtlStr(stmt) - case stmt: NewInstStmt => newInstStmt2firrtlStr(indent, stmt) + "\n" + case stmt: WeakStmt => indent + stmt2firrtlStr(stmt) + case stmt: NewInstance => newInstStmt2firrtlStr(indent, stmt) + "\n" case stmt: VarDecls => indent + varDecl2firrtlStr(indent, stmt) } - nodeStr + (tr.cld map (cld => "\n" + tree2firrtlStr(cld, indent + " "))).mkString + nodeStr + (tr.children map (cld => "\n" + tree2firrtlStr(cld, indent + " "))).mkString } /** rm module or instance names from io name, for usage in gen firrtl io section @@ -188,11 +185,11 @@ object compiler { else fullName */ - if fullName.contains(".") then + if fullName.contains(".") then { val (instNameSplit, name) = splitName(fullName) if instNameSplit == instName then "io." + name else fullName - else fullName + } else fullName } def expr2firrtlStr(expr: Expr[?]): String = { @@ -216,13 +213,13 @@ object compiler { } } - /** Compute the log2 of a Scala integer, rounded up. Useful for getting the number of - * bits needed to represent some number of states (in - 1). To get the number of bits - * needed to represent some number n, use log2Ceil(n + 1). Note: can return zero, and - * should not be used in cases where it may generate unsupported zero-width wires. + /** Compute the log2 of a Scala integer, rounded up. Useful for getting the number of bits needed + * to represent some number of states (in - 1). To get the number of bits needed to represent + * some number n, use log2Ceil(n + 1). Note: can return zero, and should not be used in cases + * where it may generate unsupported zero-width wires. * @example - * {{{ log2Ceil(1) // returns 0 log2Ceil(2) // returns 1 log2Ceil(3) // returns 2 - * log2Ceil(4) // returns 2 }}} + * {{{ log2Ceil(1) // returns 0 log2Ceil(2) // returns 1 log2Ceil(3) // returns 2 log2Ceil(4) + * // returns 2 }}} */ object log2Ceil { // (0 until n).map(_.U((1.max(log2Ceil(n))).W)) @@ -243,13 +240,13 @@ object compiler { } } - def stmt2firrtlStr(stmt: FirStmt) = { - val FirStmt(lhs, op, rhs, prefix) = stmt + def stmt2firrtlStr(stmt: WeakStmt) = { + val WeakStmt(lhs, op, rhs, prefix) = stmt val opName = firrtlOpMap.find(_._1 == op).map(_._2).getOrElse(op) prefix + expr2firrtlStr(lhs) + s" $opName ${expr2firrtlStr(rhs)}" } - def newInstStmt2firrtlStr(indent: String, stmt: NewInstStmt) = { + def newInstStmt2firrtlStr(indent: String, stmt: NewInstance) = { /* m1.clock <= clock m1.reset <= reset */ Seq( @@ -291,7 +288,7 @@ object compiler { /** convert expr to stmt bind: turn a+b into gen_ = a+b */ def expr2stmtBind(a: Expr[?]) = { val newValue = "g_" + global.getUid - FirStmt(VarLit(newValue), ":=", a, prefix = "node ") + WeakStmt(VarLit(newValue), ":=", a, prefix = "node ") } /* to A-normal form https://en.wikipedia.org/wiki/A-normal_form @@ -303,10 +300,10 @@ object compiler { stmt-> list stmt */ def stmtToSingleAssign( - stmt: FirStmt, - resList: List[FirStmt] = List() - ): List[FirStmt] = { - val FirStmt(stmtLhs, op, stmtRhs, _) = stmt + stmt: WeakStmt, + resList: List[WeakStmt] = List() + ): List[WeakStmt] = { + val WeakStmt(stmtLhs, op, stmtRhs, _) = stmt stmtRhs match { // fresh stmt for the first 2 case @@ -315,7 +312,7 @@ object compiler { stmtToSingleAssign( genStmt, List( - FirStmt( + WeakStmt( stmt.lhs, ":=", bop.copy(a = VarLit(genStmt.lhs.getname)), @@ -332,7 +329,7 @@ object compiler { */ case uop @ UniOp(a, nm) => val genStmt = expr2stmtBind(a) - val stmtNew = FirStmt( + val stmtNew = WeakStmt( stmtLhs, ":=", genStmt.lhs @@ -349,10 +346,10 @@ object compiler { } } - /** if lhs is IO,change := to <= and make new conn io.y:=a+b becomes y0=a+b;io.y<=y0 new - * : don't do above + /** if lhs is IO,change := to <= and make new conn io.y:=a+b becomes y0=a+b;io.y<=y0 new : don't + * do above */ - def IOassignTransform(stmt: FirStmt): List[FirStmt] = { + def IOassignTransform(stmt: WeakStmt): List[WeakStmt] = { stmt.lhs match { /* if lhs is IO,change := to <= and make new conn io.y:=a+b becomes y0=a+b;io.y<=y0 @@ -400,8 +397,8 @@ object compiler { } } - /** modify names for io: check if instantiated instance have same name, if so refer to - * it by io.a, otherwise add inst name as prefix + /** modify names for io: check if instantiated instance have same name, if so refer to it by io.a, + * otherwise add inst name as prefix */ def ioNameTransform(thisInstName: String, ioFullName: String) = { val instName :: name :: Nil = ioFullName.split('.').toList: @unchecked @@ -432,7 +429,7 @@ object compiler { */ def cmdsTransform(thisInstName: String, cmdList: List[Cmds]): List[Cmds] = { cmdList map { - case x @ FirStmt(lhs, op, rhs, prefix) => + case x @ WeakStmt(lhs, op, rhs, prefix) => val newStmt = x.copy( lhs = varNameTransform(thisInstName, lhs), rhs = exprTransform(thisInstName, rhs) diff --git a/src/main/scala/dependentChisel/codegen/firrtlTypes.scala b/src/main/scala/dependentChisel/codegen/firrtlTypes.scala index f6be8b0..c845f51 100644 --- a/src/main/scala/dependentChisel/codegen/firrtlTypes.scala +++ b/src/main/scala/dependentChisel/codegen/firrtlTypes.scala @@ -1,8 +1,8 @@ package dependentChisel.codegen -import dependentChisel.algo.seqCmd2tree.AST +import dependentChisel.algo.stackList2tree.AST import com.doofin.stdScalaCross.* -import dependentChisel.typesAndSyntax.chiselModules.ModLocalInfo +import dependentChisel.typesAndSyntax.chiselModules.ModuleData import dependentChisel.typesAndSyntax.typesAndOps.VarType object firrtlTypes { @@ -15,7 +15,7 @@ object firrtlTypes { ) /** one firrtl module */ - case class FirrtlModule(modInfo: ModLocalInfo, io: List[IOdef], ast: AST) + case class FirrtlModule(modInfo: ModuleData, io: List[IOdef], ast: AST) /** the whole circuit with multiple modules */ case class FirrtlCircuit(mainModuleName: String, modules: List[FirrtlModule]) diff --git a/src/main/scala/dependentChisel/codegen/seqCommands.scala b/src/main/scala/dependentChisel/codegen/sequentialCommands.scala similarity index 74% rename from src/main/scala/dependentChisel/codegen/seqCommands.scala rename to src/main/scala/dependentChisel/codegen/sequentialCommands.scala index 173f1c8..7db02bd 100644 --- a/src/main/scala/dependentChisel/codegen/seqCommands.scala +++ b/src/main/scala/dependentChisel/codegen/sequentialCommands.scala @@ -5,8 +5,12 @@ import com.doofin.stdScalaCross.* import dependentChisel.typesAndSyntax.statements.* import dependentChisel.global -/** sequential commands used in chisel UserModule to build circuit */ -object seqCommands { +/** sequential commands used in chisel UserModule to build circuit + * + * nested control structures are implemented using start/end commands which is implicitly a stack, + * so we can later convert it to a tree with seqCmd2tree + */ +object sequentialCommands { type Uid = Int /** control structures like if */ @@ -17,8 +21,15 @@ object seqCommands { case Top() } - /** all sorts of sequential commands */ + /** all sorts of sequential commands + */ sealed trait Cmds + + /** represent start/end of control block + * + * @param ctrl + * @param uid + */ case class Start[CT <: Ctrl](ctrl: CT, uid: Uid) extends Cmds // uid is not used case class End[CT <: Ctrl](ctrl: CT, uid: Uid) extends Cmds @@ -26,11 +37,11 @@ object seqCommands { sealed trait AtomicCmds extends Cmds /** for new mod */ - case class NewInstStmt(instNm: String, modNm: String) extends AtomicCmds + case class NewInstance(instNm: String, modNm: String) extends AtomicCmds /** firrtl statements: weakly typed which doesn't require width of lhs = wid of rhs. */ - case class FirStmt( + case class WeakStmt( lhs: Var[?], op: String, rhs: Expr[?], diff --git a/src/main/scala/dependentChisel/codegen/typeCheck.scala b/src/main/scala/dependentChisel/codegen/typeCheck.scala index 1acfe60..e709cc5 100644 --- a/src/main/scala/dependentChisel/codegen/typeCheck.scala +++ b/src/main/scala/dependentChisel/codegen/typeCheck.scala @@ -5,7 +5,7 @@ import scala.collection.mutable import com.doofin.stdScalaJvm.* import dependentChisel.typesAndSyntax.chiselModules.* import dependentChisel.typesAndSyntax.typesAndOps.* -import dependentChisel.codegen.seqCommands.* +import dependentChisel.codegen.sequentialCommands.* /** various checks like checking width */ object typeCheck { @@ -42,7 +42,7 @@ object typeCheck { /* 1.add width field in FirStmt 2. add width in lhs var and rhs expr 3. use a map to store width of var and expr */ - case FirStmt(lhs, op, rhs, prefix) => + case WeakStmt(lhs, op, rhs, prefix) => val lr = (getExprWidth(typeMap, lhs), getExprWidth(typeMap, rhs)) match { // only check if both result are numbers case lrWidth @ (i, j) => diff --git a/src/main/scala/dependentChisel/monadic/monadicCompilers.scala b/src/main/scala/dependentChisel/monadic/monadicCompilers.scala index ff4c1ee..69df472 100644 --- a/src/main/scala/dependentChisel/monadic/monadicCompilers.scala +++ b/src/main/scala/dependentChisel/monadic/monadicCompilers.scala @@ -76,14 +76,14 @@ object monadicCompilers { then push new node into parent stack as new top elem*/ val newParNode: AST = TreeNode(ctrl) // new parent node // add this newParNode as child - parents.top.cld += newParNode + parents.top.children += newParNode parents push newParNode case End(ctrl) => // end of block, pop out one parent parents.pop() // for other stmt,just append case stmt => - parents.top.cld += TreeNode(stmt) + parents.top.children += TreeNode(stmt) } } diff --git a/src/main/scala/dependentChisel/staticAnalysis/checkUnInitAnalysis.scala b/src/main/scala/dependentChisel/staticAnalysis/checkUnInitAnalysis.scala index bc453c5..fba74cd 100644 --- a/src/main/scala/dependentChisel/staticAnalysis/checkUnInitAnalysis.scala +++ b/src/main/scala/dependentChisel/staticAnalysis/checkUnInitAnalysis.scala @@ -1,13 +1,13 @@ package dependentChisel.staticAnalysis -import dependentChisel.codegen.seqCommands.AtomicCmds +import dependentChisel.codegen.sequentialCommands.AtomicCmds import dependentChisel.staticAnalysis.MonotoneFramework.domainMapT import dependentChisel.staticAnalysis.checkUnInitLattice import dependentChisel.staticAnalysis.MonotoneFramework.MonoFrameworkT -import dependentChisel.codegen.seqCommands.NewInstStmt -import dependentChisel.codegen.seqCommands.FirStmt -import dependentChisel.codegen.seqCommands.VarDecls +import dependentChisel.codegen.sequentialCommands.NewInstance +import dependentChisel.codegen.sequentialCommands.WeakStmt +import dependentChisel.codegen.sequentialCommands.VarDecls /** check if vars have an value */ @@ -27,7 +27,7 @@ object checkUnInitAnalysis { : ((Int, mStmt, Int), domainMapT[mDomain]) => domainMapT[mDomain] = { case ((q0, cmd, q1), varmap) => cmd match { - case FirStmt(lhs, op, rhs, prefix) => + case WeakStmt(lhs, op, rhs, prefix) => if op == ":=" then varmap.updated(lhs.getname, true) else varmap // case NewInstStmt(instNm, modNm) => // case VarDecls(v) => diff --git a/src/main/scala/dependentChisel/staticAnalysis/progGraph.scala b/src/main/scala/dependentChisel/staticAnalysis/progGraph.scala index 499b1f5..c06cd42 100644 --- a/src/main/scala/dependentChisel/staticAnalysis/progGraph.scala +++ b/src/main/scala/dependentChisel/staticAnalysis/progGraph.scala @@ -2,8 +2,8 @@ package dependentChisel.staticAnalysis import com.doofin.stdScala.mainRunnable -import dependentChisel.codegen.seqCommands.* -import dependentChisel.algo.seqCmd2tree.AST +import dependentChisel.codegen.sequentialCommands.* +import dependentChisel.algo.stackList2tree.AST import dependentChisel.typesAndSyntax.typesAndOps.* import dependentChisel.* @@ -18,7 +18,7 @@ import dependentChisel.typesAndSyntax.control.* import dependentChisel.codegen.compiler.* -import algo.seqCmd2tree.* +import algo.stackList2tree.* import dependentChisel.typesAndSyntax.chiselModules.* import dependentChisel.codegen.firrtlTypes.FirrtlCircuit @@ -79,10 +79,10 @@ object progGraph extends mainRunnable { case x: Ctrl => x match { case Ctrl.Top() => - val r = ast.cld.zipWithIndex flatMap ((x, i) => ast2progGraph(q + i, x)) + val r = ast.children.zipWithIndex flatMap ((x, i) => ast2progGraph(q + i, x)) List((p, Skip, q)) ++ r.toList case _ => - val r = ast.cld.zipWithIndex flatMap ((x, i) => ast2progGraph(q + i, x)) + val r = ast.children.zipWithIndex flatMap ((x, i) => ast2progGraph(q + i, x)) List((p, Skip, q)) ++ r.toList } diff --git a/src/main/scala/dependentChisel/staticAnalysis/test.worksheet.sc b/src/main/scala/dependentChisel/staticAnalysis/test.worksheet.sc index bd86d86..b2d4911 100644 --- a/src/main/scala/dependentChisel/staticAnalysis/test.worksheet.sc +++ b/src/main/scala/dependentChisel/staticAnalysis/test.worksheet.sc @@ -1,6 +1,6 @@ import dependentChisel.typesAndSyntax.typesAndOps.Lit import dependentChisel.typesAndSyntax.typesAndOps.VarLit -import dependentChisel.codegen.seqCommands.* +import dependentChisel.codegen.sequentialCommands.* import dependentChisel.staticAnalysis.checkUnInitAnalysis import com.doofin.stdScalaCross.* @@ -25,7 +25,7 @@ val initMap: Map[String, Boolean] = */ val pg = List( - (0, FirStmt(VarLit("x"), ":=", Lit[1](1)), 1), + (0, WeakStmt(VarLit("x"), ":=", Lit[1](1)), 1), (1, Skip, 3), (0, Skip, 2), (2, Skip, 4), diff --git a/src/main/scala/dependentChisel/typesAndSyntax/chiselModules.scala b/src/main/scala/dependentChisel/typesAndSyntax/chiselModules.scala index 2be2cc8..4b7741b 100644 --- a/src/main/scala/dependentChisel/typesAndSyntax/chiselModules.scala +++ b/src/main/scala/dependentChisel/typesAndSyntax/chiselModules.scala @@ -8,8 +8,8 @@ import dependentChisel.typesAndSyntax.statements.* import dependentChisel.typesAndSyntax.control.* import dependentChisel.codegen.firrtlTypes.* -import dependentChisel.codegen.seqCommands.* -import dependentChisel.codegen.seqCommands +import dependentChisel.codegen.sequentialCommands.* +import dependentChisel.codegen.sequentialCommands import scala.reflect.ClassTag import dependentChisel.global.getUid @@ -22,51 +22,65 @@ import scala.util.Try import scala.util.Failure import scala.util.Success -/** imperative style for chisel ,record info in mutable vars inside class chiselModules +/** IR for current implementation + * + * imperative chisel like DSL, which records info in mutable vars inside class chiselModules during + * construction */ object chiselModules { + + /** global info like list of all module + * + * @param names + * @param modules + */ case class GlobalInfo( names: ArrayBuffer[String] = ArrayBuffer(), modules: ArrayBuffer[UserModule] = ArrayBuffer() ) - case class ModLocalInfo( + + /** AST(Abstract Syntax Tree) for each module, contains all info about a module + * + * @param commands: + * list of statements, to represent the circuits + */ + case class ModuleData( className: String, - thisInstanceName: String, + instanceName: String, io: ArrayBuffer[IOdef] = ArrayBuffer(), - commands: ArrayBuffer[Cmds] = ArrayBuffer(), // list of seq cmds + commands: ArrayBuffer[Cmds] = ArrayBuffer(), // list of statements typeMap: mutable.Map[Expr[?] | Var[?], Int] = mutable.Map() // list of seq cmds ) - // trait Module {} - /* function style UserModule ,for example: when {} else {} */ trait UserModule(using parent: GlobalInfo) extends UserModuleOps, UserModuleDecls { + val classSimpleName = this.getClass.getCanonicalName.split('.').last.mkString + val thisClassName = - (Try( - this.getClass.getCanonicalName.split('.').last.mkString - ) match + (Try(classSimpleName) match { case Failure(exception) => "noName" case Success(value) => value - ) + naming.getIdWithDash + }) + naming.getIdWithDash /** Name for this Instance after new class.. */ val thisInstanceName = naming.mkUidFrom(thisClassName) - if (global.debugVerbose) println(s"new inst $thisInstanceName for $thisClassName") + if (global.debugVerbose) + println(s"new inst $thisInstanceName for $thisClassName") - given modLocalInfo: ModLocalInfo = - ModLocalInfo(className = thisClassName, thisInstanceName = thisInstanceName) + given moduleData: ModuleData = + ModuleData(className = thisClassName, instanceName = thisInstanceName) // def name = this.getClass.getCanonicalName.split('.').last val globalInfo = parent def pushCmd(cmd: Cmds) = { - modLocalInfo.commands.append(cmd) + moduleData.commands.append(cmd) } def pushBlk(ctr: Ctrl)(block: => Any) = { val uid = naming.getIntId - pushCmd(seqCommands.Start(ctr, uid)) + pushCmd(sequentialCommands.Start(ctr, uid)) block - pushCmd(seqCommands.End(ctr, uid)) + pushCmd(sequentialCommands.End(ctr, uid)) } add2parent(parent, this) diff --git a/src/main/scala/dependentChisel/typesAndSyntax/control.scala b/src/main/scala/dependentChisel/typesAndSyntax/control.scala index 5824c63..9c34352 100644 --- a/src/main/scala/dependentChisel/typesAndSyntax/control.scala +++ b/src/main/scala/dependentChisel/typesAndSyntax/control.scala @@ -5,7 +5,7 @@ import com.doofin.stdScalaCross.* import dependentChisel.typesAndSyntax.typesAndOps.* import dependentChisel.typesAndSyntax.statements.* -import dependentChisel.codegen.seqCommands.* +import dependentChisel.codegen.sequentialCommands.* import dependentChisel.typesAndSyntax.chiselModules.* import dependentChisel.syntax.naming @@ -31,7 +31,7 @@ object control { def newMod[M <: UserModule](newMod: M) = { /* m1.clock <= clock m1.reset <= reset */ - pushCmd(NewInstStmt(newMod.thisInstanceName, newMod.thisClassName)) + pushCmd(NewInstance(newMod.thisInstanceName, newMod.thisClassName)) newMod } diff --git a/src/main/scala/dependentChisel/typesAndSyntax/statements.scala b/src/main/scala/dependentChisel/typesAndSyntax/statements.scala index 695a260..1b9f7ca 100644 --- a/src/main/scala/dependentChisel/typesAndSyntax/statements.scala +++ b/src/main/scala/dependentChisel/typesAndSyntax/statements.scala @@ -9,7 +9,7 @@ import com.doofin.stdScalaJvm.* import dependentChisel.* import dependentChisel.typesAndSyntax.chiselModules.* import typesAndOps.* -import dependentChisel.codegen.seqCommands.* +import dependentChisel.codegen.sequentialCommands.* import codegen.firrtlTypes.* import dependentChisel.syntax.naming import dependentChisel.misc.macros @@ -21,7 +21,7 @@ object statements { /** typed API for assign */ extension [w <: Int, V <: Var[w]](v: V) { - inline def :=(using mli: ModLocalInfo)(oth: Expr[w]) = { + inline def :=(using md: ModuleData)(oth: Expr[w]) = { val name = v.getname /* v match { @@ -32,18 +32,18 @@ object statements { // dbg(v) // dbg(oth) // mli.typeMap.addOne(v, constValueOpt[w].get) - mli.commands += FirStmt(v, ":=", oth) + md.commands += WeakStmt(v, ":=", oth) } } /** untyped API for assign */ extension (v: VarDymTyped) { - inline def :=(using mli: ModLocalInfo)(oth: Expr[?]) = { + inline def :=(using md: ModuleData)(oth: Expr[?]) = { val name = v.getname - mli.typeMap.addOne(v, v.width) + md.typeMap.addOne(v, v.width) - mli.commands += FirStmt(v, ":=", oth) + md.commands += WeakStmt(v, ":=", oth) } } } diff --git a/src/main/scala/dependentChisel/typesAndSyntax/varDecls.scala b/src/main/scala/dependentChisel/typesAndSyntax/varDecls.scala index 5c76b4d..9254919 100644 --- a/src/main/scala/dependentChisel/typesAndSyntax/varDecls.scala +++ b/src/main/scala/dependentChisel/typesAndSyntax/varDecls.scala @@ -5,7 +5,7 @@ import com.doofin.stdScalaCross.* import dependentChisel.typesAndSyntax.typesAndOps.* import dependentChisel.typesAndSyntax.statements.* -import dependentChisel.codegen.seqCommands.* +import dependentChisel.codegen.sequentialCommands.* import dependentChisel.typesAndSyntax.chiselModules.* import dependentChisel.syntax.naming @@ -41,7 +41,7 @@ object varDecls { /** allow to be called outside module */ inline def newIO[w <: Int: ValueOf](using - mli: ModLocalInfo + mli: ModuleData )( tp: VarType.Input.type | VarType.Output.type, // widthOpt: Option[Int] = None, @@ -50,7 +50,7 @@ object varDecls { val genName = naming.genNameForVar(givenName, tp) val r = VarTyped[w]( - mli.thisInstanceName + "." + genName, + mli.instanceName + "." + genName, tp ) val width = constValueOpt[w].getOrElse(valueOf[w]) // .orElse(widthOpt) @@ -61,14 +61,14 @@ object varDecls { } def newIODym[w <: Int](using - mli: ModLocalInfo + mli: ModuleData )(width: Int, tp: VarType.Input.type | VarType.Output.type, givenName: String = "") = { val genName = naming.genNameForVar(givenName, tp) val r = VarDymTyped( width, tp, - mli.thisInstanceName + "." + genName + mli.instanceName + "." + genName ) // when refered in expr , use this name mli.typeMap.addOne(r, width) @@ -89,8 +89,8 @@ object varDecls { ) val width = constValueOpt[w].getOrElse(valueOf[w]) // .orElse(widthOpt) - modLocalInfo.typeMap.addOne(r, width) - modLocalInfo.commands.append(VarDecls(r.toDym(width))) + moduleData.typeMap.addOne(r, width) + moduleData.commands.append(VarDecls(r.toDym(width))) r } @@ -98,8 +98,8 @@ object varDecls { // need to push this cmd for varDecl val genName = naming.genNameForVar(givenName, VarType.Reg) val r = VarDymTyped(width, VarType.Reg, genName) - modLocalInfo.typeMap.addOne(r, width) - modLocalInfo.commands.append(VarDecls(r)) + moduleData.typeMap.addOne(r, width) + moduleData.commands.append(VarDecls(r)) r } @@ -108,8 +108,8 @@ object varDecls { val width = init.width val genName = naming.genNameForVar(givenName, VarType.Reg) val r = VarDymTyped(width, VarType.RegInit(init), genName) - modLocalInfo.typeMap.addOne(r, width) - modLocalInfo.commands.append(VarDecls(r)) + moduleData.typeMap.addOne(r, width) + moduleData.commands.append(VarDecls(r)) r } diff --git a/src/test/scala/dependentChisel/examples/BubbleFifo.scala b/src/test/scala/dependentChisel/examples/BubbleFifo.scala index b701b4d..cba9e11 100644 --- a/src/test/scala/dependentChisel/examples/BubbleFifo.scala +++ b/src/test/scala/dependentChisel/examples/BubbleFifo.scala @@ -20,6 +20,7 @@ object BubbleFifo extends mainRunnable { val mod = makeModule { implicit p => new BubbleFifo(2, 3) // ok } + pprint.pprintln(chiselMod2firrtlCircuits(mod)) chiselMod2verilog(mod) } @@ -31,7 +32,7 @@ object BubbleFifo extends mainRunnable { val din = Input(UInt(size.W)) } */ - class WriterIO(using ModLocalInfo)(size: Int :| Positive) { + class WriterIO(using ModuleData)(size: Int :| Positive) { /** Input */ val write = newIO[1](VarType.Input) // Bool is same as UInt<1> @@ -43,7 +44,7 @@ object BubbleFifo extends mainRunnable { // val din = newIO(VarType.Input, Some(size)) } - class ReaderIO(using ModLocalInfo)(size: Int) { + class ReaderIO(using ModuleData)(size: Int) { /** Input */ val read = newIO[1](VarType.Input) // Bool() = UInt<1> @@ -83,8 +84,14 @@ object BubbleFifo extends mainRunnable { deq.dout := dataReg } - class BubbleFifo(using GlobalInfo)(size: Int :| Positive, depth: Int) - extends UserModule { + /** an typed bubble fifo example + * + * @param x + * @param size + * guaranteed to be positive + * @param depth + */ + class BubbleFifo(using GlobalInfo)(size: Int :| Positive, depth: Int) extends UserModule { val enq = new WriterIO(size) val deq = new ReaderIO(size) @@ -109,7 +116,7 @@ object BubbleFifo extends mainRunnable { buffers(depth - 1).deq.read := deq.read } - def bulkConn(using ModLocalInfo)(enq: WriterIO, enq2: WriterIO) = { + def bulkConn(using ModuleData)(enq: WriterIO, enq2: WriterIO) = { enq.din := enq2.din enq2.full := enq.full enq.write := enq2.write diff --git a/src/test/scala/dependentChisel/examples/BubbleFifoErr.scala b/src/test/scala/dependentChisel/examples/BubbleFifoErr.scala index 2ce8f3b..9783395 100644 --- a/src/test/scala/dependentChisel/examples/BubbleFifoErr.scala +++ b/src/test/scala/dependentChisel/examples/BubbleFifoErr.scala @@ -20,7 +20,7 @@ object BubbleFifoErr { chiselMod2verilog(mod) } - class WriterIO_err(size: Int)(using mli: ModLocalInfo) { + class WriterIO_err(size: Int)(using mli: ModuleData) { /** Input */ val write = newIO[1](VarType.Input) @@ -32,7 +32,7 @@ object BubbleFifoErr { val din = newIODym(size + 1, VarType.Input) // correct should be just size } - class ReaderIO(size: Int)(using mli: ModLocalInfo) { + class ReaderIO(size: Int)(using mli: ModuleData) { /** Input */ val read = newIO[1](VarType.Input) // Bool() = UInt<1> diff --git a/src/test/scala/dependentChisel/examples/ifTest.scala b/src/test/scala/dependentChisel/examples/ifTest.scala index e8f5758..151020a 100644 --- a/src/test/scala/dependentChisel/examples/ifTest.scala +++ b/src/test/scala/dependentChisel/examples/ifTest.scala @@ -13,7 +13,7 @@ import dependentChisel.typesAndSyntax.chiselModules.* import dependentChisel.codegen.compiler.* -import algo.seqCmd2tree.* +import algo.stackList2tree.* object ifTest extends mainRunnable { override def main(args: Array[String] = Array()): Unit = run