Skip to content

Commit 82011b5

Browse files
committed
Add bitwise operations for integer properties
1 parent a2d6631 commit 82011b5

4 files changed

Lines changed: 331 additions & 0 deletions

File tree

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

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,30 @@ sealed trait Property[T] extends Element { self =>
335335
): Property[T] =
336336
ev.shl(this, that)
337337

338+
/** Perform integer bitwise AND as defined by the FIRRTL Integer And Operation.
339+
*/
340+
final def &(that: Property[T])(
341+
implicit ev: PropertyBitwiseOps[Property[T]],
342+
sourceInfo: SourceInfo
343+
): Property[T] =
344+
ev.and(this, that)
345+
346+
/** Perform integer bitwise OR as defined by the FIRRTL Integer Or Operation.
347+
*/
348+
final def |(that: Property[T])(
349+
implicit ev: PropertyBitwiseOps[Property[T]],
350+
sourceInfo: SourceInfo
351+
): Property[T] =
352+
ev.or(this, that)
353+
354+
/** Perform integer bitwise NOT as defined by the FIRRTL Integer Not Operation.
355+
*/
356+
final def unary_~(
357+
implicit ev: PropertyBitwiseOps[Property[T]],
358+
sourceInfo: SourceInfo
359+
): Property[T] =
360+
ev.not(this)
361+
338362
/** Perform concatenation as defined by FIRRTL spec section List Concatenation Operation.
339363
*/
340364
final def ++(
@@ -513,6 +537,28 @@ private object PropertyExpressionHelpers {
513537
wire.asInstanceOf[Property[T]]
514538
}
515539

540+
// Helper function to create a unary Property expression IR.
541+
def unaryOp[T](
542+
sourceInfo: SourceInfo,
543+
op: fir.PropPrimOp,
544+
value: Property[T]
545+
): Property[T] = {
546+
implicit val info = sourceInfo
547+
548+
// Create a temporary Wire to assign the expression to (validates we're in a RawModule).
549+
val wire = createPropertyWire(value)(sourceInfo)
550+
551+
// Create a PropExpr with the same type and the single operand.
552+
val propExpr = ir.PropExpr(sourceInfo, value.tpe.getPropertyType(), op, List(value.ref))
553+
554+
// Add the PropAssign command.
555+
val mod = Builder.referenceUserContainer.asInstanceOf[RawModule]
556+
mod.addCommand(ir.PropAssign(sourceInfo, wire.lref, propExpr))
557+
558+
// Return the temporary Wire as the result.
559+
wire.asInstanceOf[Property[T]]
560+
}
561+
516562
// Helper function for comparison operations that return Property[Boolean].
517563
def cmpOp[T](
518564
sourceInfo: SourceInfo,
@@ -612,6 +658,50 @@ object PropertyArithmeticOps {
612658
}
613659
}
614660

661+
/** Typeclass for integral Property bitwise operations.
662+
*/
663+
@implicitNotFound("bitwise operations are not supported on Property type ${T}")
664+
sealed trait PropertyBitwiseOps[T] {
665+
def and(lhs: T, rhs: T)(implicit sourceInfo: SourceInfo): T
666+
def or(lhs: T, rhs: T)(implicit sourceInfo: SourceInfo): T
667+
def not(value: T)(implicit sourceInfo: SourceInfo): T
668+
}
669+
670+
object PropertyBitwiseOps {
671+
import PropertyExpressionHelpers._
672+
673+
// Type class instances for integral Property bitwise operations.
674+
implicit val intBitwiseOps: PropertyBitwiseOps[Property[Int]] =
675+
new PropertyBitwiseOps[Property[Int]] {
676+
def and(lhs: Property[Int], rhs: Property[Int])(implicit sourceInfo: SourceInfo) =
677+
binOp(sourceInfo, fir.IntegerAndOp, lhs, rhs)
678+
def or(lhs: Property[Int], rhs: Property[Int])(implicit sourceInfo: SourceInfo) =
679+
binOp(sourceInfo, fir.IntegerOrOp, lhs, rhs)
680+
def not(value: Property[Int])(implicit sourceInfo: SourceInfo) =
681+
unaryOp(sourceInfo, fir.IntegerNotOp, value)
682+
}
683+
684+
implicit val longBitwiseOps: PropertyBitwiseOps[Property[Long]] =
685+
new PropertyBitwiseOps[Property[Long]] {
686+
def and(lhs: Property[Long], rhs: Property[Long])(implicit sourceInfo: SourceInfo) =
687+
binOp(sourceInfo, fir.IntegerAndOp, lhs, rhs)
688+
def or(lhs: Property[Long], rhs: Property[Long])(implicit sourceInfo: SourceInfo) =
689+
binOp(sourceInfo, fir.IntegerOrOp, lhs, rhs)
690+
def not(value: Property[Long])(implicit sourceInfo: SourceInfo) =
691+
unaryOp(sourceInfo, fir.IntegerNotOp, value)
692+
}
693+
694+
implicit val bigIntBitwiseOps: PropertyBitwiseOps[Property[BigInt]] =
695+
new PropertyBitwiseOps[Property[BigInt]] {
696+
def and(lhs: Property[BigInt], rhs: Property[BigInt])(implicit sourceInfo: SourceInfo) =
697+
binOp(sourceInfo, fir.IntegerAndOp, lhs, rhs)
698+
def or(lhs: Property[BigInt], rhs: Property[BigInt])(implicit sourceInfo: SourceInfo) =
699+
binOp(sourceInfo, fir.IntegerOrOp, lhs, rhs)
700+
def not(value: Property[BigInt])(implicit sourceInfo: SourceInfo) =
701+
unaryOp(sourceInfo, fir.IntegerNotOp, value)
702+
}
703+
}
704+
615705
/** Typeclass for Property sequence operations.
616706
*/
617707
@implicitNotFound("sequence operations are not supported on Property type ${T}")

‎docs/src/explanations/properties.md‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,26 @@ on integral `Property` typed values.
150150
| `>>` | Perform shift right as defined by FIRRTL spec section Integer Shift Right Operation |
151151
| `<<` | Perform shift left as defined by FIRRTL spec section Integer Shift Left Operation |
152152

153+
#### Integer Bitwise Operations
154+
155+
The integral `Property` types, like `Property[Int]`, `Property[Long]` and
156+
`Property[BigInt]`, can also be used to build bitwise expressions in terms of
157+
`Property` values.
158+
159+
The following table lists the bitwise operators that are supported on integral
160+
`Property` typed values.
161+
162+
| Operation | Description |
163+
| --------- | ----------- |
164+
| `&` | Perform bitwise AND as defined by FIRRTL spec section Integer Bitwise And Operation |
165+
| `|` | Perform bitwise OR as defined by FIRRTL spec section Integer Bitwise Or Operation |
166+
| `~` | Perform bitwise NOT as defined by FIRRTL spec section Integer Bitwise Not Operation |
167+
168+
These operations use arbitrary-precision integer semantics. Negative values
169+
are interpreted using two's-complement sign extension, so `~x` is equivalent
170+
to `-x - 1`. The resulting property expressions remain in the FIRRTL
171+
representation.
172+
153173
#### Sequence Operations
154174

155175
The sequence `Property` types, like `Property[Seq[Int]]` support some basic

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,12 @@ case object IntegerShrOp extends PropPrimOp("integer_shr")
300300
@deprecated("All APIs in package firrtl are deprecated.", "Chisel 7.0.0")
301301
case object IntegerShlOp extends PropPrimOp("integer_shl")
302302
@deprecated("All APIs in package firrtl are deprecated.", "Chisel 7.0.0")
303+
case object IntegerAndOp extends PropPrimOp("integer_and")
304+
@deprecated("All APIs in package firrtl are deprecated.", "Chisel 7.0.0")
305+
case object IntegerOrOp extends PropPrimOp("integer_or")
306+
@deprecated("All APIs in package firrtl are deprecated.", "Chisel 7.0.0")
307+
case object IntegerNotOp extends PropPrimOp("integer_not")
308+
@deprecated("All APIs in package firrtl are deprecated.", "Chisel 7.0.0")
303309
case object ListConcatOp extends PropPrimOp("list_concat")
304310
@deprecated("All APIs in package firrtl are deprecated.", "Chisel 7.0.0")
305311
case object StringConcatOp extends PropPrimOp("string_concat")

‎src/test/scala-2/chiselTests/properties/PropertySpec.scala‎

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,221 @@ class PropertySpec extends AnyFlatSpec with Matchers with FileCheck {
946946
)
947947
}
948948

949+
behavior.of("PropertyBitwiseOps")
950+
951+
it should "support integer OR on Int, Long, and BigInt properties" in {
952+
ChiselStage.emitCHIRRTL {
953+
new RawModule {
954+
val intA = IO(Input(Property[Int]()))
955+
val intB = IO(Input(Property[Int]()))
956+
val intOr = IO(Output(Property[Int]()))
957+
val intLiteralOr = IO(Output(Property[Int]()))
958+
intOr := intA | intB
959+
intLiteralOr := Property(0xf0) | Property(5)
960+
961+
val longA = IO(Input(Property[Long]()))
962+
val longB = IO(Input(Property[Long]()))
963+
val longOr = IO(Output(Property[Long]()))
964+
val longLiteralOr = IO(Output(Property[Long]()))
965+
longOr := longA | longB
966+
longLiteralOr := Property[Long](0xff00L) | Property[Long](6L)
967+
968+
val bigIntA = IO(Input(Property[BigInt]()))
969+
val bigIntB = IO(Input(Property[BigInt]()))
970+
val bigIntOr = IO(Output(Property[BigInt]()))
971+
val bigIntLiteralOr = IO(Output(Property[BigInt]()))
972+
bigIntOr := bigIntA | bigIntB
973+
bigIntLiteralOr := Property[BigInt](0xff0000) | Property[BigInt](7)
974+
}
975+
}.fileCheck()(
976+
"""|CHECK: propassign _intOr_propExpr, integer_or(intA, intB)
977+
|CHECK: propassign intOr, _intOr_propExpr
978+
|CHECK: propassign _intLiteralOr_propExpr, integer_or(Integer(240), Integer(5))
979+
|CHECK: propassign intLiteralOr, _intLiteralOr_propExpr
980+
|CHECK: propassign _longOr_propExpr, integer_or(longA, longB)
981+
|CHECK: propassign longOr, _longOr_propExpr
982+
|CHECK: propassign _longLiteralOr_propExpr, integer_or(Integer(65280), Integer(6))
983+
|CHECK: propassign longLiteralOr, _longLiteralOr_propExpr
984+
|CHECK: propassign _bigIntOr_propExpr, integer_or(bigIntA, bigIntB)
985+
|CHECK: propassign bigIntOr, _bigIntOr_propExpr
986+
|CHECK: propassign _bigIntLiteralOr_propExpr, integer_or(Integer(16711680), Integer(7))
987+
|CHECK: propassign bigIntLiteralOr, _bigIntLiteralOr_propExpr
988+
|""".stripMargin
989+
)
990+
}
991+
992+
it should "support integer AND on Int, Long, and BigInt properties" in {
993+
ChiselStage.emitCHIRRTL {
994+
new RawModule {
995+
val intA = IO(Input(Property[Int]()))
996+
val intB = IO(Input(Property[Int]()))
997+
val intAnd = IO(Output(Property[Int]()))
998+
val intLiteralAnd = IO(Output(Property[Int]()))
999+
intAnd := intA & intB
1000+
intLiteralAnd := Property(0xf0) & Property(0x0f)
1001+
1002+
val longA = IO(Input(Property[Long]()))
1003+
val longB = IO(Input(Property[Long]()))
1004+
val longAnd = IO(Output(Property[Long]()))
1005+
val longLiteralAnd = IO(Output(Property[Long]()))
1006+
longAnd := longA & longB
1007+
longLiteralAnd := Property[Long](0xff00L) & Property[Long](0x00ffL)
1008+
1009+
val bigIntA = IO(Input(Property[BigInt]()))
1010+
val bigIntB = IO(Input(Property[BigInt]()))
1011+
val bigIntAnd = IO(Output(Property[BigInt]()))
1012+
val bigIntLiteralAnd = IO(Output(Property[BigInt]()))
1013+
bigIntAnd := bigIntA & bigIntB
1014+
bigIntLiteralAnd := Property[BigInt](0xff0000) & Property[BigInt](0x00ff00)
1015+
}
1016+
}.fileCheck()(
1017+
"""|CHECK: propassign _intAnd_propExpr, integer_and(intA, intB)
1018+
|CHECK: propassign intAnd, _intAnd_propExpr
1019+
|CHECK: propassign _intLiteralAnd_propExpr, integer_and(Integer(240), Integer(15))
1020+
|CHECK: propassign intLiteralAnd, _intLiteralAnd_propExpr
1021+
|CHECK: propassign _longAnd_propExpr, integer_and(longA, longB)
1022+
|CHECK: propassign longAnd, _longAnd_propExpr
1023+
|CHECK: propassign _longLiteralAnd_propExpr, integer_and(Integer(65280), Integer(255))
1024+
|CHECK: propassign longLiteralAnd, _longLiteralAnd_propExpr
1025+
|CHECK: propassign _bigIntAnd_propExpr, integer_and(bigIntA, bigIntB)
1026+
|CHECK: propassign bigIntAnd, _bigIntAnd_propExpr
1027+
|CHECK: propassign _bigIntLiteralAnd_propExpr, integer_and(Integer(16711680), Integer(65280))
1028+
|CHECK: propassign bigIntLiteralAnd, _bigIntLiteralAnd_propExpr
1029+
|""".stripMargin
1030+
)
1031+
}
1032+
1033+
it should "support integer NOT on Int, Long, and BigInt properties" in {
1034+
ChiselStage.emitCHIRRTL {
1035+
new RawModule {
1036+
val intA = IO(Input(Property[Int]()))
1037+
val intNot = IO(Output(Property[Int]()))
1038+
val intLiteralNot = IO(Output(Property[Int]()))
1039+
intNot := ~intA
1040+
intLiteralNot := ~Property(-3)
1041+
1042+
val longA = IO(Input(Property[Long]()))
1043+
val longNot = IO(Output(Property[Long]()))
1044+
val longLiteralNot = IO(Output(Property[Long]()))
1045+
longNot := ~longA
1046+
longLiteralNot := ~Property[Long](-9L)
1047+
1048+
val bigIntA = IO(Input(Property[BigInt]()))
1049+
val bigIntNot = IO(Output(Property[BigInt]()))
1050+
val bigIntLiteralNot = IO(Output(Property[BigInt]()))
1051+
bigIntNot := ~bigIntA
1052+
bigIntLiteralNot := ~Property[BigInt](-17)
1053+
}
1054+
}.fileCheck()(
1055+
"""|CHECK: propassign _intNot_propExpr, integer_not(intA)
1056+
|CHECK: propassign intNot, _intNot_propExpr
1057+
|CHECK: propassign _intLiteralNot_propExpr, integer_not(Integer(-3))
1058+
|CHECK: propassign intLiteralNot, _intLiteralNot_propExpr
1059+
|CHECK: propassign _longNot_propExpr, integer_not(longA)
1060+
|CHECK: propassign longNot, _longNot_propExpr
1061+
|CHECK: propassign _longLiteralNot_propExpr, integer_not(Integer(-9))
1062+
|CHECK: propassign longLiteralNot, _longLiteralNot_propExpr
1063+
|CHECK: propassign _bigIntNot_propExpr, integer_not(bigIntA)
1064+
|CHECK: propassign bigIntNot, _bigIntNot_propExpr
1065+
|CHECK: propassign _bigIntLiteralNot_propExpr, integer_not(Integer(-17))
1066+
|CHECK: propassign bigIntLiteralNot, _bigIntLiteralNot_propExpr
1067+
|""".stripMargin
1068+
)
1069+
}
1070+
1071+
it should "support nested integer bitwise expressions" in {
1072+
ChiselStage.emitCHIRRTL {
1073+
new RawModule {
1074+
val a = IO(Input(Property[Int]()))
1075+
val b = IO(Input(Property[Int]()))
1076+
val c = IO(Input(Property[Int]()))
1077+
val result = IO(Output(Property[Int]()))
1078+
result := (a | b) & ~c
1079+
}
1080+
}.fileCheck()(
1081+
"""|CHECK: propassign _result_propExpr, integer_or(a, b)
1082+
|CHECK: propassign _result_propExpr_1, integer_not(c)
1083+
|CHECK: propassign _result_propExpr_2, integer_and(_result_propExpr, _result_propExpr_1)
1084+
|CHECK: propassign result, _result_propExpr_2
1085+
|""".stripMargin
1086+
)
1087+
}
1088+
1089+
it should "reject OR for unsupported Property types" in {
1090+
assertTypeError("""
1091+
val a = Property[String]()
1092+
val b = Property[String]()
1093+
a | b
1094+
""")
1095+
assertTypeError("""
1096+
val a = Property[Seq[Int]]()
1097+
val b = Property[Seq[Int]]()
1098+
a | b
1099+
""")
1100+
assertTypeError("""
1101+
val a = Property[Int]()
1102+
val b = Property[Long]()
1103+
a | b
1104+
""")
1105+
assertTypeError("""
1106+
val a = Property[Boolean]()
1107+
val b = Property[Boolean]()
1108+
a | b
1109+
""")
1110+
assertTypeError("""
1111+
val a = Property[Double]()
1112+
val b = Property[Double]()
1113+
a | b
1114+
""")
1115+
}
1116+
1117+
it should "reject AND for unsupported Property types" in {
1118+
assertTypeError("""
1119+
val a = Property[String]()
1120+
val b = Property[String]()
1121+
a & b
1122+
""")
1123+
assertTypeError("""
1124+
val a = Property[Seq[Int]]()
1125+
val b = Property[Seq[Int]]()
1126+
a & b
1127+
""")
1128+
assertTypeError("""
1129+
val a = Property[Int]()
1130+
val b = Property[Long]()
1131+
a & b
1132+
""")
1133+
assertTypeError("""
1134+
val a = Property[Boolean]()
1135+
val b = Property[Boolean]()
1136+
a & b
1137+
""")
1138+
assertTypeError("""
1139+
val a = Property[Double]()
1140+
val b = Property[Double]()
1141+
a & b
1142+
""")
1143+
}
1144+
1145+
it should "reject NOT for unsupported Property types" in {
1146+
assertTypeError("""
1147+
val a = Property[String]()
1148+
~a
1149+
""")
1150+
assertTypeError("""
1151+
val a = Property[Seq[Int]]()
1152+
~a
1153+
""")
1154+
assertTypeError("""
1155+
val a = Property[Boolean]()
1156+
~a
1157+
""")
1158+
assertTypeError("""
1159+
val a = Property[Double]()
1160+
~a
1161+
""")
1162+
}
1163+
9491164
behavior.of("PropertySeqOps")
9501165

9511166
it should "not support expressions involving Property types that don't provide a typeclass instance" in {

0 commit comments

Comments
 (0)