Skip to content

Commit

Permalink
[Scala3] Refactor Printf and VerificationStatement macros (#4449)
Browse files Browse the repository at this point in the history
  • Loading branch information
adkian-sifive authored Oct 9, 2024
1 parent 75a095d commit 90a8d39
Show file tree
Hide file tree
Showing 4 changed files with 362 additions and 372 deletions.
89 changes: 89 additions & 0 deletions core/src/main/scala-2/chisel3/PrintfMacros.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: Apache-2.0

package chisel3

import chisel3.internal._
import chisel3.internal.Builder.pushCommand
import chisel3.experimental.SourceInfo
import scala.language.experimental.macros
import scala.reflect.macros.blackbox

object PrintfMacrosCompat {
def _applyMacroWithInterpolatorCheck(
c: blackbox.Context
)(fmt: c.Tree,
data: c.Tree*
)(sourceInfo: c.Tree
): c.Tree = {
import c.universe._
_checkFormatString(c)(fmt)
val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("printfWithReset"))
q"$apply_impl_do(_root_.chisel3.Printable.pack($fmt, ..$data))($sourceInfo)"
}

private[chisel3] def _checkFormatString(c: blackbox.Context)(fmt: c.Tree): Unit = {
import c.universe._

val errorString = "The s-interpolator prints the Scala .toString of Data objects rather than the value " +
"of the hardware wire during simulation. Use the cf-interpolator instead. If you want " +
"an elaboration time print, use println."

// Error on Data in the AST by matching on the Scala 2.13 string
// interpolation lowering to concatenation
def throwOnChiselData(x: c.Tree): Unit = x match {
case q"$x+$y" => {
if (x.tpe <:< typeOf[chisel3.Data] || y.tpe <:< typeOf[chisel3.Data]) {
c.error(c.enclosingPosition, errorString)
} else {
throwOnChiselData(x)
throwOnChiselData(y)
}
}
case _ =>
}
throwOnChiselData(fmt)

fmt match {
case q"scala.StringContext.apply(..$_).s(..$_)" =>
c.error(
c.enclosingPosition,
errorString
)
case _ =>
}
}

private[chisel3] def printfWithReset(
pable: Printable
)(
implicit sourceInfo: SourceInfo
): chisel3.printf.Printf = {
var printfId: chisel3.printf.Printf = null
when(!Module.reset.asBool) {
printfId = printfWithoutReset(pable)
}
printfId
}

private[chisel3] def printfWithoutReset(
pable: Printable
)(
implicit sourceInfo: SourceInfo
): chisel3.printf.Printf = {
val clock = Builder.forcedClock
val printfId = new chisel3.printf.Printf(pable)

Printable.checkScope(pable)

pushCommand(chisel3.internal.firrtl.ir.Printf(printfId, sourceInfo, clock.ref, pable))
printfId
}

private[chisel3] def printfWithoutReset(
fmt: String,
data: Bits*
)(
implicit sourceInfo: SourceInfo
): chisel3.printf.Printf =
printfWithoutReset(Printable.pack(fmt, data: _*))
}
240 changes: 240 additions & 0 deletions core/src/main/scala-2/chisel3/VerificationStatementMacros.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
// SPDX-License-Identifier: Apache-2.0

package chisel3

import chisel3.experimental.SourceInfo
import chisel3.PrintfMacrosCompat._
import chisel3.internal.firrtl.ir._
import chisel3.layer.block
import chisel3.layers
import chisel3.util.circt.IfElseFatalIntrinsic
import chisel3.internal.Builder.pushCommand
import chisel3.internal._

import scala.language.experimental.macros
import scala.reflect.macros.blackbox
import scala.reflect.macros.blackbox.Context
import scala.annotation.nowarn

object VerifStmtMacrosCompat {

type SourceLineInfo = (String, Int)

private[chisel3] def formatFailureMessage(
kind: String,
lineInfo: SourceLineInfo,
cond: Bool,
message: Option[Printable]
)(
implicit sourceInfo: SourceInfo
): Printable = {
val (filename, line) = lineInfo
val lineMsg = s"$filename:$line".replaceAll("%", "%%")
message match {
case Some(msg) =>
p"$kind failed: $msg\n"
case None => p"$kind failed at $lineMsg\n"
}
}

private[chisel3] def getLine(c: blackbox.Context): SourceLineInfo = {
val p = c.enclosingPosition
(p.source.file.name, p.line): @nowarn // suppress, there's no clear replacement
}

object assert {

/** @group VerifPrintMacros */
def _applyMacroWithInterpolatorCheck(
c: blackbox.Context
)(cond: c.Tree,
message: c.Tree,
data: c.Tree*
)(sourceInfo: c.Tree
): c.Tree = {
import c.universe._
_checkFormatString(c)(message)
val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some(_root_.chisel3.Printable.pack($message, ..$data)))($sourceInfo)"
}

/** @group VerifPrintMacros */
def _applyMacroWithStringMessage(
c: blackbox.Context
)(cond: c.Tree,
message: c.Tree,
data: c.Tree
)(sourceInfo: c.Tree
): c.Tree = {
import c.universe._
val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
q"$apply_impl_do($cond, ${getLine(c)},_root_.scala.Some(_root_.chisel3.Printable.pack($message,..$data)))($sourceInfo)"
}

/** @group VerifPrintMacros */
def _applyMacroWithPrintableMessage(
c: blackbox.Context
)(cond: c.Tree,
message: c.Tree
)(sourceInfo: c.Tree
): c.Tree = {
import c.universe._
val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some($message))($sourceInfo)"
}

/** @group VerifPrintMacros */
def _applyMacroWithNoMessage(
c: blackbox.Context
)(cond: c.Tree
)(sourceInfo: c.Tree
): c.Tree = {
import c.universe._
val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.None)($sourceInfo)"
}

/** @group VerifPrintMacros */
def _applyWithSourceLinePrintable(
cond: Bool,
line: SourceLineInfo,
message: Option[Printable]
)(
implicit sourceInfo: SourceInfo
): chisel3.assert.Assert = {
message.foreach(Printable.checkScope(_))
val pable = formatFailureMessage("Assertion", line, cond, message)
emitIfElseFatalIntrinsic(Module.clock, cond, !Module.reset.asBool, pable)
}

private def emitIfElseFatalIntrinsic(
clock: Clock,
predicate: Bool,
enable: Bool,
format: Printable
)(
implicit sourceInfo: SourceInfo
): chisel3.assert.Assert = {
block(layers.Verification.Assert, skipIfAlreadyInBlock = true, skipIfLayersEnabled = true) {
val id = Builder.forcedUserModule // It should be safe since we push commands anyway.
IfElseFatalIntrinsic(id, format, "chisel3_builtin", clock, predicate, enable, format.unpackArgs: _*)(sourceInfo)
}
new chisel3.assert.Assert()
}
}

object assume {

/** @group VerifPrintMacros */
def _applyMacroWithInterpolatorCheck(
c: blackbox.Context
)(cond: c.Tree,
message: c.Tree,
data: c.Tree*
)(sourceInfo: c.Tree
): c.Tree = {
import c.universe._
_checkFormatString(c)(message)
val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some(_root_.chisel3.Printable.pack($message, ..$data)))($sourceInfo)"
}

/** @group VerifPrintMacros */
def _applyMacroWithStringMessage(
c: blackbox.Context
)(cond: c.Tree,
message: c.Tree,
data: c.Tree
)(sourceInfo: c.Tree
): c.Tree = {
import c.universe._
val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some(_root_.chisel3.Printable.pack($message, ..$data)))($sourceInfo)"
}

/** @group VerifPrintMacros */
def _applyMacroWithPrintableMessage(
c: blackbox.Context
)(cond: c.Tree,
message: c.Tree
)(sourceInfo: c.Tree
): c.Tree = {
import c.universe._
val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some($message))($sourceInfo)"
}

/** @group VerifPrintMacros */
def _applyMacroWithNoMessage(
c: blackbox.Context
)(cond: c.Tree
)(sourceInfo: c.Tree
): c.Tree = {
import c.universe._
val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable"))
q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.None)($sourceInfo)"
}

/** @group VerifPrintMacros */
def _applyWithSourceLinePrintable(
cond: Bool,
line: SourceLineInfo,
message: Option[Printable]
)(
implicit sourceInfo: SourceInfo
): chisel3.assume.Assume = {
val id = new chisel3.assume.Assume()
block(layers.Verification.Assume, skipIfAlreadyInBlock = true, skipIfLayersEnabled = true) {
message.foreach(Printable.checkScope(_))
when(!Module.reset.asBool) {
val formattedMsg = formatFailureMessage("Assumption", line, cond, message)
Builder.pushCommand(Verification(id, Formal.Assume, sourceInfo, Module.clock.ref, cond.ref, formattedMsg))
}
}
id
}
}

object cover {

/** @group VerifPrintMacros */
def _applyMacroWithNoMessage(
c: blackbox.Context
)(cond: c.Tree
)(sourceInfo: c.Tree
): c.Tree = {
import c.universe._
val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLine"))
q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.None)($sourceInfo)"
}

/** @group VerifPrintMacros */
def _applyMacroWithMessage(
c: blackbox.Context
)(cond: c.Tree,
message: c.Tree
)(sourceInfo: c.Tree
): c.Tree = {
import c.universe._
val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLine"))
q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some($message))($sourceInfo)"
}

/** @group VerifPrintMacros */
def _applyWithSourceLine(
cond: Bool,
line: SourceLineInfo,
message: Option[String]
)(
implicit sourceInfo: SourceInfo
): chisel3.cover.Cover = {
val id = new chisel3.cover.Cover()
block(layers.Verification.Cover, skipIfAlreadyInBlock = true, skipIfLayersEnabled = true) {
when(!Module.reset.asBool) {
Builder.pushCommand(Verification(id, Formal.Cover, sourceInfo, Module.clock.ref, cond.ref, ""))
}
}
id
}
}
}
Loading

0 comments on commit 90a8d39

Please sign in to comment.