// SPDX-License-Identifier: Apache-2.0 package chisel3 import scala.reflect.macros.blackbox.Context import scala.language.experimental.macros import chisel3.internal._ import chisel3.internal.Builder.pushCommand import chisel3.internal.firrtl._ import chisel3.internal.sourceinfo.SourceInfo import scala.reflect.macros.blackbox /** Scaladoc information for internal verification statement macros * that are used in objects assert, assume and cover. * * @groupdesc VerifPrintMacros * *

* '''These internal methods are not part of the public-facing API!''' *

*
* * @groupprio VerifPrintMacros 1001 */ trait VerifPrintMacrosDoc object assert extends VerifPrintMacrosDoc { /** Checks for a condition to be valid in the circuit at rising clock edge * when not in reset. If the condition evaluates to false, the circuit * simulation stops with an error. * * @param cond condition, assertion fires (simulation fails) when false * @param message optional format string to print when the assertion fires * @param data optional bits to print in the message formatting * * @note See [[printf.apply(fmt:String* printf]] for format string documentation * @note currently cannot be used in core Chisel / libraries because macro * defs need to be compiled first and the SBT project is not set up to do * that */ // Macros currently can't take default arguments, so we need two functions to emulate defaults. def apply( cond: Bool, message: String, data: Bits* )( implicit sourceInfo: SourceInfo, compileOptions: CompileOptions ): Assert = macro _applyMacroWithInterpolatorCheck /** Checks for a condition to be valid in the circuit at all times. If the * condition evaluates to false, the circuit simulation stops with an error. * * Does not fire when in reset (defined as the current implicit reset, e.g. as set by * the enclosing `withReset` or Module.reset. * * May be called outside of a Module (like defined in a function), so * functions using assert make the standard Module assumptions (single clock * and single reset). * * @param cond condition, assertion fires (simulation fails) on a rising clock edge when false and reset is not asserted * @param message optional chisel Printable type message * * @note See [[printf.apply(fmt:Printable)]] for documentation on printf using Printables * @note currently cannot be used in core Chisel / libraries because macro * defs need to be compiled first and the SBT project is not set up to do * that */ def apply( cond: Bool, message: Printable )( implicit sourceInfo: SourceInfo, compileOptions: CompileOptions ): Assert = macro _applyMacroWithPrintableMessage def apply(cond: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Assert = macro _applyMacroWithNoMessage import VerificationStatement._ /** @group VerifPrintMacros */ def _applyMacroWithInterpolatorCheck( c: blackbox.Context )(cond: c.Tree, message: c.Tree, data: c.Tree* )(sourceInfo: c.Tree, compileOptions: c.Tree ): c.Tree = { import c.universe._ message match { case q"scala.StringContext.apply(..$_).s(..$_)" => c.warning( c.enclosingPosition, "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 check, call assert with a Boolean condition." ) case _ => } 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, $compileOptions)" } /** An elaboration-time assertion. Calls the built-in Scala assert function. */ def apply(cond: Boolean, message: => String): Unit = Predef.assert(cond, message) /** An elaboration-time assertion. Calls the built-in Scala assert function. */ def apply(cond: Boolean): Unit = Predef.assert(cond, "") /** Named class for assertions. */ final class Assert private[chisel3] () extends VerificationStatement /** @group VerifPrintMacros */ @deprecated( "This method has been deprecated in favor of _applyMacroWithStringMessage. Please use the same.", "Chisel 3.5" ) def _applyMacroWithMessage( c: blackbox.Context )(cond: c.Tree, message: c.Tree, data: c.Tree* )(sourceInfo: c.Tree, compileOptions: 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, $compileOptions)" } /** @group VerifPrintMacros */ def _applyMacroWithStringMessage( c: blackbox.Context )(cond: c.Tree, message: c.Tree, data: c.Tree* )(sourceInfo: c.Tree, compileOptions: 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, $compileOptions)" } /** @group VerifPrintMacros */ def _applyMacroWithPrintableMessage( c: blackbox.Context )(cond: c.Tree, message: c.Tree )(sourceInfo: c.Tree, compileOptions: 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, $compileOptions)" } /** @group VerifPrintMacros */ def _applyMacroWithNoMessage( c: blackbox.Context )(cond: c.Tree )(sourceInfo: c.Tree, compileOptions: 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, $compileOptions)" } /** This will be removed in Chisel 3.6 in favor of the Printable version * * @group VerifPrintMacros */ def _applyWithSourceLine( cond: Bool, line: SourceLineInfo, message: Option[String], data: Bits* )( implicit sourceInfo: SourceInfo, compileOptions: CompileOptions ): Assert = { val id = new Assert() when(!Module.reset.asBool()) { failureMessage("Assertion", line, cond, message.map(Printable.pack(_, data: _*))) Builder.pushCommand(Verification(id, Formal.Assert, sourceInfo, Module.clock.ref, cond.ref, "")) } id } /** @group VerifPrintMacros */ def _applyWithSourceLinePrintable( cond: Bool, line: SourceLineInfo, message: Option[Printable] )( implicit sourceInfo: SourceInfo, compileOptions: CompileOptions ): Assert = { val id = new Assert() message.foreach(Printable.checkScope(_)) when(!Module.reset.asBool()) { failureMessage("Assertion", line, cond, message) Builder.pushCommand(Verification(id, Formal.Assert, sourceInfo, Module.clock.ref, cond.ref, "")) } id } } object assume extends VerifPrintMacrosDoc { /** Assumes a condition to be valid in the circuit at all times. * Acts like an assertion in simulation and imposes a declarative * assumption on the state explored by formal tools. * * Does not fire when in reset (defined as the encapsulating Module's * reset). If your definition of reset is not the encapsulating Module's * reset, you will need to gate this externally. * * May be called outside of a Module (like defined in a function), so * functions using assert make the standard Module assumptions (single clock * and single reset). * * @param cond condition, assertion fires (simulation fails) when false * @param message optional format string to print when the assertion fires * @param data optional bits to print in the message formatting * * @note See [[printf.apply(fmt:String* printf]] for format string documentation */ // Macros currently can't take default arguments, so we need two functions to emulate defaults. def apply( cond: Bool, message: String, data: Bits* )( implicit sourceInfo: SourceInfo, compileOptions: CompileOptions ): Assume = macro _applyMacroWithInterpolatorCheck /** Assumes a condition to be valid in the circuit at all times. * Acts like an assertion in simulation and imposes a declarative * assumption on the state explored by formal tools. * * Does not fire when in reset (defined as the encapsulating Module's * reset). If your definition of reset is not the encapsulating Module's * reset, you will need to gate this externally. * * May be called outside of a Module (like defined in a function), so * functions using assert make the standard Module assumptions (single clock * and single reset). * * @param cond condition, assertion fires (simulation fails) when false * @param message optional Printable type message when the assertion fires * * @note See [[printf.apply(fmt:Printable]] for documentation on printf using Printables */ def apply( cond: Bool, message: Printable )( implicit sourceInfo: SourceInfo, compileOptions: CompileOptions ): Assume = macro _applyMacroWithPrintableMessage def apply(cond: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Assume = macro _applyMacroWithNoMessage /** An elaboration-time assumption. Calls the built-in Scala assume function. */ def apply(cond: Boolean, message: => String): Unit = Predef.assume(cond, message) /** An elaboration-time assumption. Calls the built-in Scala assume function. */ def apply(cond: Boolean): Unit = Predef.assume(cond, "") /** Named class for assumptions. */ final class Assume private[chisel3] () extends VerificationStatement import VerificationStatement._ /** @group VerifPrintMacros */ def _applyMacroWithInterpolatorCheck( c: blackbox.Context )(cond: c.Tree, message: c.Tree, data: c.Tree* )(sourceInfo: c.Tree, compileOptions: c.Tree ): c.Tree = { import c.universe._ message match { case q"scala.StringContext.apply(..$_).s(..$_)" => c.warning( c.enclosingPosition, "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 check, call assert with a Boolean condition." ) case _ => } 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, $compileOptions)" } /** @group VerifPrintMacros */ @deprecated( "This method has been deprecated in favor of _applyMacroWithStringMessage. Please use the same.", "Chisel 3.5" ) def _applyMacroWithMessage( c: blackbox.Context )(cond: c.Tree, message: c.Tree, data: c.Tree* )(sourceInfo: c.Tree, compileOptions: 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, $compileOptions)" } /** @group VerifPrintMacros */ def _applyMacroWithStringMessage( c: blackbox.Context )(cond: c.Tree, message: c.Tree, data: c.Tree* )(sourceInfo: c.Tree, compileOptions: 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, $compileOptions)" } /** @group VerifPrintMacros */ def _applyMacroWithPrintableMessage( c: blackbox.Context )(cond: c.Tree, message: c.Tree )(sourceInfo: c.Tree, compileOptions: 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, $compileOptions)" } /** @group VerifPrintMacros */ def _applyMacroWithNoMessage( c: blackbox.Context )(cond: c.Tree )(sourceInfo: c.Tree, compileOptions: 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, $compileOptions)" } /** This will be removed in Chisel 3.6 in favor of the Printable version * * @group VerifPrintMacros */ def _applyWithSourceLine( cond: Bool, line: SourceLineInfo, message: Option[String], data: Bits* )( implicit sourceInfo: SourceInfo, compileOptions: CompileOptions ): Assume = { val id = new Assume() when(!Module.reset.asBool()) { failureMessage("Assumption", line, cond, message.map(Printable.pack(_, data: _*))) Builder.pushCommand(Verification(id, Formal.Assume, sourceInfo, Module.clock.ref, cond.ref, "")) } id } /** @group VerifPrintMacros */ def _applyWithSourceLinePrintable( cond: Bool, line: SourceLineInfo, message: Option[Printable] )( implicit sourceInfo: SourceInfo, compileOptions: CompileOptions ): Assume = { val id = new Assume() message.foreach(Printable.checkScope(_)) when(!Module.reset.asBool()) { failureMessage("Assumption", line, cond, message) Builder.pushCommand(Verification(id, Formal.Assume, sourceInfo, Module.clock.ref, cond.ref, "")) } id } } object cover extends VerifPrintMacrosDoc { /** Declares a condition to be covered. * At ever clock event, a counter is incremented iff the condition is active * and reset is inactive. * * Does not fire when in reset (defined as the encapsulating Module's * reset). If your definition of reset is not the encapsulating Module's * reset, you will need to gate this externally. * * May be called outside of a Module (like defined in a function), so * functions using assert make the standard Module assumptions (single clock * and single reset). * * @param cond condition that will be sampled on every clock tick * @param message a string describing the cover event */ // Macros currently can't take default arguments, so we need two functions to emulate defaults. def apply(cond: Bool, message: String)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Cover = macro _applyMacroWithMessage def apply(cond: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Cover = macro _applyMacroWithNoMessage /** Named class for cover statements. */ final class Cover private[chisel3] () extends VerificationStatement import VerificationStatement._ /** @group VerifPrintMacros */ def _applyMacroWithNoMessage( c: blackbox.Context )(cond: c.Tree )(sourceInfo: c.Tree, compileOptions: 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, $compileOptions)" } /** @group VerifPrintMacros */ def _applyMacroWithMessage( c: blackbox.Context )(cond: c.Tree, message: c.Tree )(sourceInfo: c.Tree, compileOptions: 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, $compileOptions)" } /** @group VerifPrintMacros */ def _applyWithSourceLine( cond: Bool, line: SourceLineInfo, message: Option[String] )( implicit sourceInfo: SourceInfo, compileOptions: CompileOptions ): Cover = { val id = new Cover() when(!Module.reset.asBool()) { Builder.pushCommand(Verification(id, Formal.Cover, sourceInfo, Module.clock.ref, cond.ref, "")) } id } } object stop { /** Terminate execution, indicating success. * * @param message a string describing why the simulation was stopped */ def apply(message: String = "")(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Stop = { val stp = new Stop() when(!Module.reset.asBool) { pushCommand(Stop(stp, sourceInfo, Builder.forcedClock.ref, 0)) } stp } /** Terminate execution with a failure code. */ @deprecated( "Non-zero return codes are not well supported. Please use assert(false.B) if you want to indicate a failure.", "Chisel 3.5" ) def apply(code: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Stop = { val stp = new Stop() when(!Module.reset.asBool) { pushCommand(Stop(stp, sourceInfo, Builder.forcedClock.ref, code)) } stp } /** Named class for [[stop]]s. */ final class Stop private[chisel3] () extends VerificationStatement } /** Base class for all verification statements: Assert, Assume, Cover, Stop and Printf. */ abstract class VerificationStatement extends NamedComponent { _parent.foreach(_.addId(this)) } /** Helper functions for common functionality required by stop, assert, assume or cover */ private object VerificationStatement { type SourceLineInfo = (String, Int, String) def getLine(c: blackbox.Context): SourceLineInfo = { val p = c.enclosingPosition (p.source.file.name, p.line, p.lineContent.trim) } def failureMessage( kind: String, lineInfo: SourceLineInfo, cond: Bool, message: Option[Printable] )( implicit sourceInfo: SourceInfo, compileOptions: CompileOptions ): Unit = { val (filename, line, content) = lineInfo val lineMsg = s"$filename:$line $content".replaceAll("%", "%%") val fmt = message match { case Some(msg) => p"$kind failed: $msg\n at $lineMsg\n" case None => p"$kind failed\n at $lineMsg\n" } when(!cond) { printf.printfWithoutReset(fmt) } } }