Skip to content

Commit c9c0e68

Browse files
committed
[core] Add Property[String] assertion message
Add an alternative property assertion API which takes a `Property[String]` message. This builds towards allowing for string interpolation in property assertion messages. Due to the limited availability of property operations, this is relatively limited in power and very verbose. More operations (e.g., number to string formatting) and better ergonomics are planned. Assisted-by: pi.dev:gpt-5.6-luna Signed-off-by: Schuyler Eldridge <schuyler.eldridge@sifive.com>
1 parent 269f211 commit c9c0e68

6 files changed

Lines changed: 44 additions & 14 deletions

File tree

core/src/main/scala/chisel3/internal/firrtl/Converter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ private[chisel3] object Converter {
193193
case PropAssign(info, loc, exp) =>
194194
fir.PropAssign(convert(info), convert(loc, ctx, info), convert(exp, ctx, info))
195195
case PropertyAssert(info, cond, msg) =>
196-
fir.PropertyAssert(convert(info), convert(cond, ctx, info), msg)
196+
fir.PropertyAssert(convert(info), convert(cond, ctx, info), convert(msg, ctx, info))
197197
case Attach(info, locs) =>
198198
fir.Attach(convert(info), locs.map(l => convert(l, ctx, info)))
199199
case DefInvalid(info, arg) =>

core/src/main/scala/chisel3/internal/firrtl/IR.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ private[chisel3] object ir {
482482

483483
case class Connect(sourceInfo: SourceInfo, loc: Arg, exp: Arg) extends Command
484484
case class PropAssign(sourceInfo: SourceInfo, loc: Node, exp: Arg) extends Command
485-
case class PropertyAssert(sourceInfo: SourceInfo, condition: Arg, message: String) extends Command
485+
case class PropertyAssert(sourceInfo: SourceInfo, condition: Arg, message: Arg) extends Command
486486
case class Attach(sourceInfo: SourceInfo, locs: Seq[Node]) extends Command
487487
case class Stop(id: stop.Stop, sourceInfo: SourceInfo, clock: Arg, ret: Int) extends Definition
488488

core/src/main/scala/chisel3/internal/firrtl/Serializer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ private[chisel3] object Serializer {
274274
case PropAssign(info, loc, exp) =>
275275
b ++= "propassign "; serialize(loc, ctx, info); b ++= ", "; serialize(exp, ctx, info); serialize(info)
276276
case PropertyAssert(info, cond, msg) =>
277-
b ++= "propassert "; serialize(cond, ctx, info); b ++= ", "; b ++= fir.StringLit(msg).escape; serialize(info)
277+
b ++= "propassert "; serialize(cond, ctx, info); b ++= ", "; serialize(msg, ctx, info); serialize(info)
278278
case Attach(info, locs) =>
279279
b ++= "attach ("
280280
serializeArgs(locs, ctx, info)

core/src/main/scala/chisel3/properties/Property.scala

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -347,14 +347,33 @@ sealed trait Property[T] extends Element { self =>
347347
}
348348

349349
/** Assert that this boolean property holds (only available for Property[Boolean])
350+
*
351+
* This can be used to build up interpolated string messages as opposed to
352+
* purely static messages.
353+
*
354+
* @param message a string property message
355+
*/
356+
final def assert[U](message: Property[U])(
357+
implicit evT: T =:= Boolean,
358+
evU: U =:= String,
359+
sourceInfo: SourceInfo
360+
): Unit = {
361+
PropertyBooleanOps.assert(this.asInstanceOf[Property[Boolean]], message.asInstanceOf[Property[String]])
362+
}
363+
364+
/** Assert that this boolean property holds (only available for Property[Boolean])
365+
*
366+
* This is a legacy method that supports a constant assertion message. If
367+
* you need an interpolated message, use the alternative assert that takes a
368+
* `Property[String]` message.
350369
*
351370
* @param message the assertion message
352371
*/
353372
final def assert(message: String)(
354-
implicit ev: T =:= Boolean,
355-
sourceInfo: SourceInfo
373+
implicit evT: T =:= Boolean,
374+
sourceInfo: SourceInfo
356375
): Unit = {
357-
PropertyBooleanOps.assert(this.asInstanceOf[Property[Boolean]], message)
376+
PropertyBooleanOps.assert(this.asInstanceOf[Property[Boolean]], Property[String](message))
358377
}
359378

360379
/** Property equality comparison
@@ -685,12 +704,12 @@ object PropertyBooleanOps {
685704
* @param cond the boolean property condition
686705
* @param message the assertion message to display if the assertion fails
687706
*/
688-
def assert(cond: Property[Boolean], message: String)(implicit sourceInfo: SourceInfo): Unit =
707+
def assert(cond: Property[Boolean], message: Property[String])(implicit sourceInfo: SourceInfo): Unit =
689708
Builder.referenceUserContainer match {
690709
case rm: RawModule =>
691-
rm.addCommand(firrtl.ir.PropertyAssert(sourceInfo, cond.ref, message))
710+
rm.addCommand(firrtl.ir.PropertyAssert(sourceInfo, cond.ref, message.ref))
692711
case cls: Class =>
693-
cls.addCommand(firrtl.ir.PropertyAssert(sourceInfo, cond.ref, message))
712+
cls.addCommand(firrtl.ir.PropertyAssert(sourceInfo, cond.ref, message.ref))
694713
}
695714
}
696715

firrtl/src/main/scala/firrtl/ir/IR.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ case class Connect(info: Info, loc: Expression, expr: Expression) extends Statem
421421
@deprecated("All APIs in package firrtl are deprecated.", "Chisel 7.0.0")
422422
case class PropAssign(info: Info, loc: Expression, expr: Expression) extends Statement with HasInfo with UseSerializer
423423
@deprecated("All APIs in package firrtl are deprecated.", "Chisel 7.0.0")
424-
case class PropertyAssert(info: Info, condition: Expression, message: String)
424+
case class PropertyAssert(info: Info, condition: Expression, message: Expression)
425425
extends Statement
426426
with HasInfo
427427
with UseSerializer

src/test/scala/chiselTests/properties/PropertyAssertSpec.scala

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,15 @@ class PropertyAssertSpec extends AnyFlatSpec with Matchers with FileCheck {
1717
ChiselStage
1818
.emitCHIRRTL(new RawModule {
1919
val prop = IO(Input(Property[Boolean]()))
20+
val message = IO(Input(Property[String]()))
2021
prop.assert("must be true")
22+
prop.assert(message)
2123
})
2224
.fileCheck() {
2325
"""|CHECK: input prop : Bool
24-
|CHECK: propassert prop, "must be true"
26+
|CHECK: input message : String
27+
|CHECK: propassert prop, String("must be true")
28+
|CHECK: propassert prop, message
2529
|""".stripMargin
2630
}
2731
}
@@ -32,21 +36,28 @@ class PropertyAssertSpec extends AnyFlatSpec with Matchers with FileCheck {
3236
Definition(new Class {
3337
override def desiredName = "TestClass"
3438
val prop = IO(Input(Property[Boolean]()))
39+
val message = IO(Input(Property[String]()))
3540
prop.assert("must be true")
41+
prop.assert(message)
3642
})
3743
})
3844
.fileCheck() {
3945
"""|CHECK: class TestClass :
4046
|CHECK: input prop : Bool
41-
|CHECK: propassert prop, "must be true"
47+
|CHECK: input message : String
48+
|CHECK: propassert prop, String("must be true")
49+
|CHECK: propassert prop, message
4250
|""".stripMargin
4351
}
4452
}
4553

4654
it should "compile to SystemVerilog" in {
47-
ChiselStage.emitSystemVerilog(new RawModule {
55+
class Foo extends RawModule {
4856
val prop = IO(Input(Property[Boolean]()))
57+
val username = IO(Input(Property[String]()))
4958
prop.assert("must be true")
50-
})
59+
prop.assert(Property("Hello ") ++ username ++ Property("!"))
60+
}
61+
ChiselStage.emitSystemVerilog(new Foo)
5162
}
5263
}

0 commit comments

Comments
 (0)