Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for sequences of Properties, including values. #3491

Merged
merged 1 commit into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions core/src/main/scala/chisel3/internal/Binding.scala
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,4 @@ case class BundleLitBinding(litMap: Map[Data, LitArg]) extends LitBinding
@deprecated(deprecatedPublicAPIMsg, "Chisel 3.6")
case class VecLitBinding(litMap: VectorMap[Data, LitArg]) extends LitBinding
// Literal binding attached to a Property.
private[chisel3] case class PropertyLitBinding(litProp: PropertyLit[_])
extends UnconstrainedBinding
with ReadOnlyBinding
private[chisel3] case object PropertyValueBinding extends UnconstrainedBinding with ReadOnlyBinding
23 changes: 18 additions & 5 deletions core/src/main/scala/chisel3/internal/firrtl/Converter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,17 @@ private[chisel3] object Converter {
case PropertyLit(lit: Long) => fir.IntegerPropertyLiteral(lit)
case PropertyLit(lit: BigInt) => fir.IntegerPropertyLiteral(lit)
case PropertyLit(lit: String) => fir.StringPropertyLiteral(lit)
// TODO can this be merged with PropertySeqValue?
case lit @ PropertyLit(seq: Seq[_]) =>
val pt = lit.propertyType
val values = seq.map(PropertyLit[Any](_)(pt)).map(convert(_, ctx, info))
// We know we have a Seq so do the unsafe cast because we need the element type out of it
val tpe = extractType(pt.getPropertyType.asInstanceOf[SequencePropertyType].elementType)
fir.SequencePropertyValue(tpe, values)
case value @ PropertySeqValue(args) =>
val values = args.map(convert(_, ctx, info))
val tpe = extractType(value.propertyType.getPropertyType)
fir.SequencePropertyValue(tpe, values)
case e @ ProbeExpr(probe) =>
fir.ProbeExpr(convert(probe, ctx, info))
case e @ RWProbeExpr(probe) =>
Expand Down Expand Up @@ -335,6 +346,12 @@ private[chisel3] object Converter {

def extractType(baseType: BaseType, info: SourceInfo): fir.Type = extractType(baseType, false, info, true, true)

def extractType(tpe: PropertyType): fir.PropertyType = tpe match {
case IntegerPropertyType => fir.IntegerPropertyType
case StringPropertyType => fir.StringPropertyType
case SequencePropertyType(elementType) => fir.SequencePropertyType(extractType(elementType))
}

def extractType(
baseType: BaseType,
clearDir: Boolean,
Expand Down Expand Up @@ -380,11 +397,7 @@ private[chisel3] object Converter {
else
extractType(t._elements.head._2, childClearDir, info, checkProbe, true)
}
case t: Property[_] =>
t.getPropertyType match {
case IntegerPropertyType => fir.IntegerPropertyType
case StringPropertyType => fir.StringPropertyType
}
case t: Property[_] => extractType(t.getPropertyType)
}

def convert(name: String, param: Param): fir.Param = param match {
Expand Down
12 changes: 11 additions & 1 deletion core/src/main/scala/chisel3/internal/firrtl/IR.scala
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,24 @@ private[chisel3] case class PropertyLit[T: PropertyTypeclass](lit: T) extends Ar
def minWidth: Int = 0
def cloneWithWidth(newWidth: Width): this.type = PropertyLit[T](lit).asInstanceOf[this.type]

def propertyType: PropertyTypeclass[T] = implicitly[PropertyTypeclass[T]]

/** Expose a bindLitArg API for PropertyLit, similar to LitArg.
*/
def bindLitArg(elem: Property[T]): Property[T] = {
elem.bind(PropertyLitBinding(this))
elem.bind(PropertyValueBinding)
elem.setRef(this)
elem
}
}

private[chisel3] case class PropertySeqValue[T: PropertyTypeclass](values: Seq[Arg]) extends Arg {
// TODO in theory this should be the valid FIRRTL representation
def name: String = s"PropertySeqValue($values)"

def propertyType: PropertyTypeclass[T] = implicitly[PropertyTypeclass[T]]
}

@deprecated(deprecatedPublicAPIMsg, "Chisel 3.6")
case class Ref(name: String) extends Arg

Expand Down Expand Up @@ -296,6 +305,7 @@ object MemPortDirection {
private[chisel3] sealed trait PropertyType
private[chisel3] case object IntegerPropertyType extends PropertyType
private[chisel3] case object StringPropertyType extends PropertyType
private[chisel3] case class SequencePropertyType(elementType: PropertyType) extends PropertyType

@deprecated(deprecatedPublicAPIMsg, "Chisel 3.6")
abstract class Command {
Expand Down
26 changes: 25 additions & 1 deletion core/src/main/scala/chisel3/properties/Property.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,17 @@
package chisel3.properties

import chisel3.{ActualDirection, BaseType, MonoConnectException, SpecifiedDirection}
import chisel3.internal.{checkConnect, throwException, Binding, Builder, MonoConnect, ReadOnlyBinding, TopBinding}
import chisel3.internal.{
checkConnect,
throwException,
Binding,
Builder,
MonoConnect,
PortBinding,
PropertyValueBinding,
ReadOnlyBinding,
TopBinding
}
import chisel3.internal.{firrtl => ir}
import chisel3.experimental.{prefix, requireIsHardware, SourceInfo}
import scala.reflect.runtime.universe.{typeOf, TypeTag}
Expand Down Expand Up @@ -44,6 +54,10 @@ private[chisel3] object PropertyType {
implicit val stringPropertyTypeInstance = new PropertyType[String] {
override def getPropertyType: ir.PropertyType = ir.StringPropertyType
}

implicit def sequencePropertyTypeInstance[A: PropertyType, F[_] <: Seq[_]] = new PropertyType[F[A]] {
override def getPropertyType: ir.PropertyType = ir.SequencePropertyType(implicitly[PropertyType[A]].getPropertyType)
}
}

/** Property is the base type for all properties.
Expand Down Expand Up @@ -159,4 +173,14 @@ object Property {
val result = new Property[T]
literal.bindLitArg(result)
}

/** Create a new Seq Property value
*/
def apply[T: PropertyType, F[_] <: Seq[_]](seq: F[Property[T]]): Property[F[T]] = {
val values = seq.asInstanceOf[Seq[Property[T]]].map(p => p.ref)
val result = new Property[F[T]]
result.bind(PropertyValueBinding)
result.setRef(ir.PropertySeqValue[T](values))
result
}
}
26 changes: 22 additions & 4 deletions docs/src/explanations/properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ The following are legal `Property` types:
* `Property[Long]`
* `Property[BigInt]`
* `Property[String]`
* `Property[Seq[A]]` (where `A` is itself a `Property`)

## Using Properties

Expand Down Expand Up @@ -77,15 +78,32 @@ Connections are only supported between the same `Property` type. For example, a
`Property[Int]` may only be connected to a `Property[Int]`. This is enforced by
the Scala compiler.

### Property Literals
### Property Values

The legal `Property` types may be used to construct literals by applying the
`Property` object to a literal value of the `Property` type. For example, a
`Property` literal may be connected to an output `Property` type port:
The legal `Property` types may be used to construct values by applying the
`Property` object to a value of the `Property` type. For example, a
`Property` value may be connected to an output `Property` type port:

```scala mdoc:silent
class LiteralExample extends RawModule {
val outPort = IO(Output(Property[Int]()))
outPort := Property(123)
}
```

### Property Sequences

Similarly to the primitive `Property` types, sequences of `Properties` may also be
for creating ports and values and they may also be connected:

```scala mdoc:silent
class SequenceExample extends RawModule {
val inPort = IO(Input(Property[Int]()))
val outPort1 = IO(Output(Property[Seq[Int]]()))
val outPort2 = IO(Output(Property[Seq[Int]]()))
// A Seq of literals can by turned into a Property
outPort1 := Property(Seq(123, 456))
// Property ports and literals can be mixed together into a Seq
outPort2 := Property(Seq(inPort, Property(789)))
}
```
10 changes: 8 additions & 2 deletions firrtl/src/main/scala/firrtl/ir/IR.scala
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ case class StringPropertyLiteral(value: String) extends Expression with UseSeria
val width = UnknownWidth
}

case class SequencePropertyValue(tpe: Type, values: Seq[Expression]) extends Expression with UseSerializer

case class DoPrim(op: PrimOp, args: Seq[Expression], consts: Seq[BigInt], tpe: Type)
extends Expression
with UseSerializer
Expand Down Expand Up @@ -513,9 +515,13 @@ case object AsyncResetType extends GroundType with UseSerializer {
}
case class AnalogType(width: Width) extends GroundType with UseSerializer

case object IntegerPropertyType extends Type with UseSerializer
sealed abstract class PropertyType extends Type with UseSerializer

case object IntegerPropertyType extends PropertyType

case object StringPropertyType extends PropertyType

case object StringPropertyType extends Type with UseSerializer
case class SequencePropertyType(tpe: PropertyType) extends PropertyType

case object UnknownType extends Type with UseSerializer

Expand Down
30 changes: 20 additions & 10 deletions firrtl/src/main/scala/firrtl/ir/Serializer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@ object Serializer {
b ++= "Integer("; b ++= value.toString(10); b ++= ")"
case StringPropertyLiteral(value) =>
b ++= "String(\""; b ++= value; b ++= "\")"
case SequencePropertyValue(tpe, values) =>
b ++= "List<"; s(tpe); b ++= ">(";
val lastIdx = values.size - 1
values.zipWithIndex.foreach {
case (value, idx) =>
s(value)
if (idx != lastIdx) b ++= ", "
}
b += ')'
case ProbeExpr(expr, _) => b ++= "probe("; s(expr); b += ')'
case RWProbeExpr(expr, _) => b ++= "rwprobe("; s(expr); b += ')'
case ProbeRead(expr, _) => b ++= "read("; s(expr); b += ')'
Expand Down Expand Up @@ -357,16 +366,17 @@ object Serializer {
}
case UIntType(width: Width) => b ++= "UInt"; s(width)
case SIntType(width: Width) => b ++= "SInt"; s(width)
case BundleType(fields) => b ++= "{ "; sField(fields, ", "); b += '}'
case VectorType(tpe, size) => s(tpe, lastEmittedConst); b += '['; b ++= size.toString; b += ']'
case ClockType => b ++= "Clock"
case ResetType => b ++= "Reset"
case AsyncResetType => b ++= "AsyncReset"
case AnalogType(width) => b ++= "Analog"; s(width)
case IntegerPropertyType => b ++= "Integer"
case StringPropertyType => b ++= "String"
case UnknownType => b += '?'
case other => b ++= other.serialize // Handle user-defined nodes
case BundleType(fields) => b ++= "{ "; sField(fields, ", "); b += '}'
case VectorType(tpe, size) => s(tpe, lastEmittedConst); b += '['; b ++= size.toString; b += ']'
case ClockType => b ++= "Clock"
case ResetType => b ++= "Reset"
case AsyncResetType => b ++= "AsyncReset"
case AnalogType(width) => b ++= "Analog"; s(width)
case IntegerPropertyType => b ++= "Integer"
case StringPropertyType => b ++= "String"
case SequencePropertyType(tpe) => b ++= "List<"; s(tpe, lastEmittedConst); b += '>'
case UnknownType => b += '?'
case other => b ++= other.serialize // Handle user-defined nodes
}

private def s(node: Direction)(implicit b: StringBuilder, indent: Int): Unit = node match {
Expand Down
47 changes: 47 additions & 0 deletions src/test/scala/chiselTests/properties/PropertySpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,51 @@ class PropertySpec extends ChiselFlatSpec with MatchesAndOmits {
}
""")
}

it should "support Seq[Int], Vector[Int], and List[Int] as a Property type" in {
val chirrtl = ChiselStage.emitCHIRRTL(new RawModule {
val seqProp1 = IO(Input(Property[Seq[Int]]()))
val seqProp2 = IO(Input(Property[Vector[Int]]()))
val seqProp3 = IO(Input(Property[List[Int]]()))
})

matchesAndOmits(chirrtl)(
"input seqProp1 : List<Integer>",
"input seqProp2 : List<Integer>",
"input seqProp3 : List<Integer>"
)()
}

it should "support nested Seqs as a Property type" in {
val chirrtl = ChiselStage.emitCHIRRTL(new RawModule {
val nestedSeqProp = IO(Input(Property[Seq[Seq[Seq[Int]]]]()))
})

matchesAndOmits(chirrtl)(
"input nestedSeqProp : List<List<List<Integer>>>"
)()
}

it should "support Seq[BigInt] as Property values" in {
val chirrtl = ChiselStage.emitCHIRRTL(new RawModule {
val propOut = IO(Output(Property[Seq[BigInt]]()))
propOut := Property(Seq[BigInt](123, 456)) // The Int => BigInt implicit conversion fails here
})

matchesAndOmits(chirrtl)(
"propassign propOut, List<Integer>(Integer(123), Integer(456))"
)()
}

it should "support mixed Seqs of Integer literal and ports as Seq Property values" in {
val chirrtl = ChiselStage.emitCHIRRTL(new RawModule {
val propIn = IO(Input(Property[BigInt]()))
val propOut = IO(Output(Property[Seq[BigInt]]()))
propOut := Property(Seq(propIn, Property(BigInt(123))))
})

matchesAndOmits(chirrtl)(
"propassign propOut, List<Integer>(propIn, Integer(123))"
)()
}
}