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

API for probing internal signals #3088

Merged
merged 37 commits into from
Apr 26, 2023
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
1ab8b58
adding probes to ir/serializer
debs-sifive Mar 14, 2023
6b9aa53
scalafmt
debs-sifive Mar 14, 2023
4a4aa22
attempt to make spec example more sensical
debs-sifive Mar 15, 2023
c56375d
breaking up serializer test
debs-sifive Mar 16, 2023
b33198b
Merge branch 'main' into probes
azidar Mar 28, 2023
f1791fa
Merge branch 'chipsalliance:main' into probes
debs-sifive Apr 5, 2023
c64d9ae
first draft of user API
debs-sifive Apr 5, 2023
2161ae0
fixing probespec checks
debs-sifive Apr 5, 2023
df04b4f
adding test for connectors
debs-sifive Apr 5, 2023
75aeb23
pass of reviews
debs-sifive Apr 6, 2023
d6c3288
adding port binding check
debs-sifive Apr 7, 2023
e5ef2a5
moving probes out of experimental package
debs-sifive Apr 10, 2023
d8b6e0d
adding isProbe to DataMirror
debs-sifive Apr 10, 2023
5409404
fix refs
debs-sifive Apr 10, 2023
b43a759
fix probes with vecs
debs-sifive Apr 10, 2023
1bf49cb
adding scaladoc + stronger checks
debs-sifive Apr 10, 2023
3b327be
passiveness coercion for probes
debs-sifive Apr 10, 2023
44bea7e
clean up Data; add notes
debs-sifive Apr 13, 2023
1576292
change api to RW/Probe() and RW/ProbeValue() object
debs-sifive Apr 14, 2023
f82c6e7
Merge branch 'main' of github.com:chipsalliance/chisel3 into probes
debs-sifive Apr 17, 2023
f328002
fixing probe modifier checks for wire/reg/mem
debs-sifive Apr 17, 2023
d7ae759
added macros, make probe of probe illegal
debs-sifive Apr 17, 2023
51d599e
moving force/release methods into RWProbe only
debs-sifive Apr 17, 2023
a03531a
create probe package
debs-sifive Apr 17, 2023
522708e
fix converter merge
debs-sifive Apr 18, 2023
f590e1f
support accesses to probe subfields
debs-sifive Apr 21, 2023
1ba978f
test case for define type checking
debs-sifive Apr 21, 2023
733b5d9
forbid probe of aggregate containing probes
debs-sifive Apr 24, 2023
f4367af
fixing const extractType
debs-sifive Apr 24, 2023
0766aff
handling connections for probes
debs-sifive Apr 25, 2023
939d1bf
address schuyler's review
debs-sifive Apr 26, 2023
59f0ac3
additional tests and checks
debs-sifive Apr 26, 2023
957585b
add sourceInfo to Const
debs-sifive Apr 26, 2023
c4f0c60
fix probeInfoVar
debs-sifive Apr 26, 2023
92a4143
grouping macros for scaladoc
debs-sifive Apr 26, 2023
a9d5988
address jack's review
debs-sifive Apr 26, 2023
5f0031e
fix scaladoc groupings
debs-sifive Apr 26, 2023
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
3 changes: 2 additions & 1 deletion core/src/main/scala/chisel3/Const.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
package chisel3

import chisel3._
import chisel3.internal.{requireIsChiselType, Builder}
import chisel3.internal.{requireIsChiselType, requireNoProbeTypeModifier, Builder}

/** Create a constant type in FIRRTL, which is guaranteed to take a single
* constant value.
Expand All @@ -13,6 +13,7 @@ object Const {
val prevId = Builder.idGen.value
val data = source // should only evaluate source once
requireIsChiselType(data)
requireNoProbeTypeModifier(data, "Cannot create Const of a Probe.")
val ret = if (!data.mustClone(prevId)) data else data.cloneType.asInstanceOf[T]
ret.isConst = true
ret
Expand Down
15 changes: 14 additions & 1 deletion core/src/main/scala/chisel3/Data.scala
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,8 @@ object Flipped {
* @define coll data
*/
abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
import Data.ProbeInfo

// This is a bad API that punches through object boundaries.
private[chisel3] def flatten: IndexedSeq[Element] = {
this match {
Expand Down Expand Up @@ -372,6 +374,12 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
}
}

// probeInfo only exists if this is a probe type
private var _probeInfoVar: ProbeInfo = null
private var _probeInfo: Option[ProbeInfo] = Option(_probeInfoVar)
private[chisel3] def probeInfo: Option[ProbeInfo] = _probeInfo
private[chisel3] def probeInfo_=(probeInfo: Option[ProbeInfo]) = _probeInfo = probeInfo

// If this Data is constant, it must hold a constant value
private var _isConst: Boolean = false
private[chisel3] def isConst: Boolean = _isConst
Expand Down Expand Up @@ -659,12 +667,13 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
/** Internal API; Chisel users should look at chisel3.chiselTypeOf(...).
*
* Returns a copy of this data type, with hardware bindings (if any) removed.
* Directionality data is still preserved.
* Directionality data and probe information is still preserved.
*/
private[chisel3] def cloneTypeFull: this.type = {
val clone = this.cloneType // get a fresh object, without bindings
// Only the top-level direction needs to be fixed up, cloneType should do the rest
clone.specifiedDirection = specifiedDirection
probe.setProbeModifier(clone, probeInfo)
clone.isConst = isConst
clone
}
Expand Down Expand Up @@ -774,6 +783,8 @@ object Data {
// Needed for the `implicit def toConnectableDefault`
import scala.language.implicitConversions

private[chisel3] case class ProbeInfo(val writable: Boolean)

/** Provides :<=, :>=, :<>=, and :#= between consumer and producer of the same T <: Data */
implicit class ConnectableDefault[T <: Data](consumer: T) extends connectable.ConnectableOperators[T](consumer)

Expand Down Expand Up @@ -927,6 +938,8 @@ trait WireFactory {
val prevId = Builder.idGen.value
val t = source // evaluate once (passed by name)
requireIsChiselType(t, "wire type")
requireNoProbeTypeModifier(t, "Cannot make a wire of a Chisel type with a probe modifier.")

val x = if (!t.mustClone(prevId)) t else t.cloneTypeFull

// Bind each element of x to being a Wire
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/scala/chisel3/Mem.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt, sourceInf

if (t.isConst) Builder.error("Mem type cannot be const.")(sourceInfo)

requireNoProbeTypeModifier(t, "Cannot make a Mem of a Chisel type with a probe modifier.")(sourceInfo)

_parent.foreach(_.addId(this))

// if the memory is created in a scope with an implicit clock (-> clockInst is defined), we will perform checks that
Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala/chisel3/RawModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ abstract class RawModule extends BaseModule {
id.forceName(default = "REG", _namespace)
case WireBinding(_, _) =>
id.forceName(default = "_WIRE", _namespace)
// probes have their refs set eagerly
case _ => // don't name literals
}
} // else, don't name unbound types
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/scala/chisel3/Reg.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ object Reg {
val t = source // evaluate once (passed by name)
requireIsChiselType(t, "reg type")
if (t.isConst) Builder.error("Cannot create register with constant value.")(sourceInfo)
requireNoProbeTypeModifier(t, "Cannot make a register of a Chisel type with a probe modifier.")
val reg = if (!t.mustClone(prevId)) t else t.cloneTypeFull
val clock = Node(Builder.forcedClock)

Expand Down Expand Up @@ -177,6 +178,7 @@ object RegInit {

reg.bind(RegBinding(Builder.forcedUserModule, Builder.currentWhen))
requireIsHardware(init, "reg initializer")
requireNoProbeTypeModifier(t, "Cannot make a register of a Chisel type with a probe modifier.")
pushCommand(DefRegInit(sourceInfo, reg, clock.ref, reset.ref, init.ref))
reg
}
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/scala/chisel3/connectable/Connectable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ final class Connectable[+T <: Data] private (
this.copy(excluded = excluded ++ excludedMembers.toSet).asInstanceOf[Connectable[S]]
}

/** Exclude probes */
def excludeProbes: Connectable[T] = excludeEach {
case f if (DataMirror.hasProbeTypeModifier(f)) => Seq(f)
}

/** Add any elements of members that are OpaqueType */
private def addOpaque(members: Seq[Data]): Seq[Data] = {
members.flatMap {
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/scala/chisel3/connectable/Connection.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import chisel3.internal.Builder.pushCommand
import chisel3.internal.firrtl.DefInvalid
import chisel3.experimental.{prefix, SourceInfo, UnlocatableSourceInfo}
import chisel3.experimental.{attach, Analog}
import chisel3.reflect.DataMirror.hasProbeTypeModifier
import Alignment.matchingZipOfChildren

import scala.collection.mutable
Expand Down Expand Up @@ -234,6 +235,10 @@ private[chisel3] object Connection {
deriveChildAlignment(f, proAlign)
)
}
// Check that neither consumer nor producer contains probes
case (consumer: Data, producer: Data)
if (hasProbeTypeModifier(consumer) || hasProbeTypeModifier(producer)) =>
errors = "Cannot use connectables with probe types. Exclude them prior to connection." +: errors
case (consumer, producer) =>
val alignment = (
conAlign.alignsWith(proAlign),
Expand Down
10 changes: 10 additions & 0 deletions core/src/main/scala/chisel3/internal/BiConnect.scala
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ private[chisel3] object BiConnect {
)
def DontCareCantBeSink =
BiConnectException(": DontCare cannot be a connection sink (LHS)")
def LeftProbeBiConnectionException(left: Data) =
BiConnectException(s"Left of Probed type cannot participate in a bi connection (<>)")
def RightProbeBiConnectionException(right: Data) =
BiConnectException(s"Right of Probed type cannot participate in a bi connection (<>)")

/** This function is what recursively tries to connect a left and right together
*
Expand All @@ -81,6 +85,12 @@ private[chisel3] object BiConnect {
context_mod: RawModule
): Unit = {
(left, right) match {
// Disallow monoconnecting Probe types
case (left_e: Data, _) if containsProbe(left_e) =>
throw LeftProbeBiConnectionException(left_e)
case (_, right_e: Data) if containsProbe(right_e) =>
throw RightProbeBiConnectionException(right_e)

// Handle element case (root case)
case (left_a: Analog, right_a: Analog) =>
try {
Expand Down
11 changes: 11 additions & 0 deletions core/src/main/scala/chisel3/internal/MonoConnect.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package chisel3.internal

import chisel3._
import chisel3.experimental.{Analog, BaseModule, SourceInfo}
import chisel3.internal.containsProbe
import chisel3.internal.Builder.pushCommand
import chisel3.internal.firrtl.{Connect, Converter, DefInvalid}
import chisel3.experimental.dataview.{isView, reify, reifyToAggregate}
Expand Down Expand Up @@ -73,6 +74,10 @@ private[chisel3] object MonoConnect {
MonoConnectException(
s"Source ${formatName(source)} and sink ${formatName(sink)} of type Analog cannot participate in a mono connection (:=)"
)
def SourceProbeMonoConnectionException(source: Data) =
MonoConnectException(s"Source ${formatName(source)} of Probed type cannot participate in a mono connection (:=)")
def SinkProbeMonoConnectionException(sink: Data) =
MonoConnectException(s"Sink ${formatName(sink)} of Probed type cannot participate in a mono connection (:=)")

def checkWhenVisibility(x: Data): Boolean = {
x.topBinding match {
Expand All @@ -97,6 +102,12 @@ private[chisel3] object MonoConnect {
): Unit = {
(sink, source) match {

// Disallow monoconnecting Probe types
case (_, source_e: Data) if containsProbe(source_e) =>
throw SourceProbeMonoConnectionException(source_e)
case (sink_e: Data, _) if containsProbe(sink_e) =>
throw SinkProbeMonoConnectionException(sink_e)

// Handle legal element cases, note (Bool, Bool) is caught by the first two, as Bool is a UInt
case (sink_e: Bool, source_e: UInt) =>
elemConnect(sourceInfo, sink_e, source_e, context_mod)
Expand Down
66 changes: 57 additions & 9 deletions core/src/main/scala/chisel3/internal/firrtl/Converter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ private[chisel3] object Converter {
// TODO Simplify
case lit: ILit =>
throw new InternalErrorException(s"Unexpected ILit: $lit")
case e @ ProbeExpr(probe) =>
fir.ProbeExpr(convert(probe, ctx, info))
case e @ RWProbeExpr(probe) =>
fir.RWProbeExpr(convert(probe, ctx, info))
case e @ ProbeRead(probe) =>
fir.ProbeRead(convert(probe, ctx, info))
case other =>
throw new InternalErrorException(s"Unexpected type in convert $other")
}
Expand Down Expand Up @@ -175,6 +181,31 @@ private[chisel3] object Converter {
e.name
)
)
case e @ ProbeDefine(sourceInfo, sink, probeExpr) =>
Some(fir.ProbeDefine(convert(sourceInfo), convert(sink, ctx, sourceInfo), convert(probeExpr, ctx, sourceInfo)))
case e @ ProbeForceInitial(sourceInfo, probe, value) =>
Some(fir.ProbeForceInitial(convert(sourceInfo), convert(probe, ctx, sourceInfo), convert(value, ctx, sourceInfo)))
case e @ ProbeReleaseInitial(sourceInfo, probe) =>
Some(fir.ProbeReleaseInitial(convert(sourceInfo), convert(probe, ctx, sourceInfo)))
case e @ ProbeForce(sourceInfo, clock, cond, probe, value) =>
Some(
fir.ProbeForce(
convert(sourceInfo),
convert(clock, ctx, sourceInfo),
convert(cond, ctx, sourceInfo),
convert(probe, ctx, sourceInfo),
convert(value, ctx, sourceInfo)
)
)
case e @ ProbeRelease(sourceInfo, clock, cond, probe) =>
Some(
fir.ProbeRelease(
convert(sourceInfo),
convert(clock, ctx, sourceInfo),
convert(cond, ctx, sourceInfo),
convert(probe, ctx, sourceInfo)
)
)
case e @ Verification(_, op, info, clk, pred, msg) =>
val firOp = op match {
case Formal.Assert => fir.Formal.Assert
Expand Down Expand Up @@ -298,10 +329,24 @@ private[chisel3] object Converter {
case d => d.specifiedDirection
}

def extractType(data: Data, info: SourceInfo): fir.Type = extractType(data, false, info)
def extractType(data: Data, info: SourceInfo): fir.Type = extractType(data, false, info, true, true)

def extractType(data: Data, clearDir: Boolean, info: SourceInfo, checkConst: Boolean = true): fir.Type = data match {
case _ if (checkConst && data.isConst) => fir.ConstType(extractType(data, clearDir, info, false))
def extractType(
data: Data,
clearDir: Boolean,
info: SourceInfo,
checkProbe: Boolean,
checkConst: Boolean
): fir.Type = data match {
// extract underlying type for probe
case _ if (checkProbe && data.probeInfo.nonEmpty) =>
if (data.probeInfo.get.writable) {
fir.RWProbeType(extractType(data, clearDir, info, false, checkConst))
} else {
fir.ProbeType(extractType(data, clearDir, info, false, checkConst))
}
// extract underlying type for const
case _ if (checkConst && data.isConst) => fir.ConstType(extractType(data, clearDir, info, checkProbe, false))
case _: Clock => fir.ClockType
case _: AsyncReset => fir.AsyncResetType
case _: ResetType => fir.ResetType
Expand All @@ -312,21 +357,24 @@ private[chisel3] object Converter {
case d: Vec[_] =>
val childClearDir = clearDir ||
d.specifiedDirection == SpecifiedDirection.Input || d.specifiedDirection == SpecifiedDirection.Output
fir.VectorType(extractType(d.sample_element, childClearDir, info), d.length)
// if Vector is a probe, don't emit Probe<...> on its elements
fir.VectorType(extractType(d.sample_element, childClearDir, info, checkProbe, true), d.length)
case d: Record => {
val childClearDir = clearDir ||
d.specifiedDirection == SpecifiedDirection.Input || d.specifiedDirection == SpecifiedDirection.Output
// if Record is a probe, don't emit Probe<...> on its elements
def eltField(elt: Data): fir.Field = (childClearDir, firrtlUserDirOf(elt)) match {
case (true, _) => fir.Field(getRef(elt, info).name, fir.Default, extractType(elt, true, info))
case (true, _) =>
fir.Field(getRef(elt, info).name, fir.Default, extractType(elt, true, info, checkProbe, true))
case (false, SpecifiedDirection.Unspecified | SpecifiedDirection.Output) =>
fir.Field(getRef(elt, info).name, fir.Default, extractType(elt, false, info))
fir.Field(getRef(elt, info).name, fir.Default, extractType(elt, false, info, checkProbe, true))
case (false, SpecifiedDirection.Flip | SpecifiedDirection.Input) =>
fir.Field(getRef(elt, info).name, fir.Flip, extractType(elt, false, info))
fir.Field(getRef(elt, info).name, fir.Flip, extractType(elt, false, info, checkProbe, true))
}
if (!d._isOpaqueType)
fir.BundleType(d._elements.toIndexedSeq.reverse.map { case (_, e) => eltField(e) })
else
extractType(d._elements.head._2, childClearDir, info)
extractType(d._elements.head._2, childClearDir, info, checkProbe, true)
}
}

Expand All @@ -347,7 +395,7 @@ private[chisel3] object Converter {
case SpecifiedDirection.Input | SpecifiedDirection.Output => true
case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip => false
}
val tpe = extractType(port.id, clearDir, port.sourceInfo)
val tpe = extractType(port.id, clearDir, port.sourceInfo, true, true)
fir.Port(convert(port.sourceInfo), getRef(port.id, port.sourceInfo).name, dir, tpe)
}

Expand Down
16 changes: 16 additions & 0 deletions core/src/main/scala/chisel3/internal/firrtl/IR.scala
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,14 @@ case class Index(imm: Arg, value: Arg) extends Arg {
override def localName: String = s"${imm.localName}[${value.localName}]"
}

sealed trait ProbeDetails { this: Arg =>
val probe: Arg
override def name: String = s"$probe"
}
private[chisel3] case class ProbeExpr(probe: Arg) extends Arg with ProbeDetails
private[chisel3] case class RWProbeExpr(probe: Arg) extends Arg with ProbeDetails
private[chisel3] case class ProbeRead(probe: Arg) extends Arg with ProbeDetails

@deprecated(deprecatedPublicAPIMsg, "Chisel 3.6")
object Width {
def apply(x: Int): Width = KnownWidth(x)
Expand Down Expand Up @@ -308,6 +316,7 @@ case class DefMemPort[T <: Data](
index: Arg,
clock: Arg)
extends Definition

@nowarn("msg=class Port") // delete when Port becomes private
@deprecated(deprecatedPublicAPIMsg, "Chisel 3.6")
case class DefInstance(sourceInfo: SourceInfo, id: BaseModule, ports: Seq[Port]) extends Definition
Expand Down Expand Up @@ -337,6 +346,13 @@ case class Port(id: Data, dir: SpecifiedDirection, sourceInfo: SourceInfo)
@deprecated(deprecatedPublicAPIMsg, "Chisel 3.6")
case class Printf(id: printf.Printf, sourceInfo: SourceInfo, clock: Arg, pable: Printable) extends Definition

private[chisel3] case class ProbeDefine(sourceInfo: SourceInfo, sink: Arg, probe: Arg) extends Command
private[chisel3] case class ProbeForceInitial(sourceInfo: SourceInfo, probe: Arg, value: Arg) extends Command
private[chisel3] case class ProbeReleaseInitial(sourceInfo: SourceInfo, probe: Arg) extends Command
private[chisel3] case class ProbeForce(sourceInfo: SourceInfo, clock: Arg, cond: Arg, probe: Arg, value: Arg)
extends Command
private[chisel3] case class ProbeRelease(sourceInfo: SourceInfo, clock: Arg, cond: Arg, probe: Arg) extends Command

@deprecated(deprecatedPublicAPIMsg, "Chisel 3.6")
object Formal extends Enumeration {
val Assert = Value("assert")
Expand Down
40 changes: 39 additions & 1 deletion core/src/main/scala/chisel3/internal/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
package chisel3

import firrtl.annotations.{IsModule, ModuleTarget}
import chisel3.experimental.{BaseModule, UnlocatableSourceInfo}
import chisel3.experimental.{BaseModule, SourceInfo, UnlocatableSourceInfo}
import chisel3.reflect.DataMirror.hasProbeTypeModifier
import chisel3.internal.firrtl.{Component, DefModule}
import chisel3.internal.Builder.Prefix

Expand Down Expand Up @@ -101,4 +102,41 @@ package object internal {
*/
private[chisel3] val ViewParent =
Module.do_apply(new ViewParentAPI)(UnlocatableSourceInfo)

private[chisel3] def requireHasProbeTypeModifier(
probe: Data,
errorMessage: String = ""
)(
implicit sourceInfo: SourceInfo
): Unit = {
val msg = if (errorMessage.isEmpty) s"Expected a probe." else errorMessage
if (!hasProbeTypeModifier(probe)) Builder.error(msg)
}

private[chisel3] def requireNoProbeTypeModifier(
probe: Data,
errorMessage: String = ""
)(
implicit sourceInfo: SourceInfo
): Unit = {
val msg = if (errorMessage.isEmpty) s"Did not expect a probe." else errorMessage
if (hasProbeTypeModifier(probe)) Builder.error(msg)
}

private[chisel3] def requireHasWritableProbeTypeModifier(
probe: Data,
errorMessage: String = ""
)(
implicit sourceInfo: SourceInfo
): Unit = {
val msg = if (errorMessage.isEmpty) s"Expected a writable probe." else errorMessage
requireHasProbeTypeModifier(probe)
if (!probe.probeInfo.get.writable) Builder.error(msg)
}

private[chisel3] def containsProbe(data: Data): Boolean = data match {
case a: Aggregate =>
a.elementsIterator.foldLeft(false)((res: Boolean, d: Data) => res || containsProbe(d))
case leaf => leaf.probeInfo.nonEmpty
}
}
Loading