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 9 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
9 changes: 9 additions & 0 deletions 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,11 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
}
}

// Denotes if this is a probe type
private var _probeInfo: ProbeInfo = ProbeInfo(false, false)
private[chisel3] def probeInfo: ProbeInfo = _probeInfo
private[chisel3] def probeInfo_=(probeInfo: ProbeInfo) = _probeInfo = probeInfo

// User-specified direction, local at this node only.
// Note that the actual direction of this node can differ from child and parent specifiedDirection.
private var _specifiedDirection: SpecifiedDirection = SpecifiedDirection.Unspecified
Expand Down Expand Up @@ -760,6 +767,8 @@ object Data {
// Needed for the `implicit def toConnectableDefault`
import scala.language.implicitConversions

case class ProbeInfo(val isProbe: Boolean, 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
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
88 changes: 88 additions & 0 deletions core/src/main/scala/chisel3/experimental/Probe.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// SPDX-License-Identifier: Apache-2.0

package chisel3.experimental

import chisel3._
import chisel3.Data.ProbeInfo
import chisel3.internal.Builder
import chisel3.internal.Builder.pushCommand
import chisel3.internal.firrtl._

// import firrtl.ir._

/** Utilities for creating and working with probe types.
*/
object Probe {

/** Create a probe type from a Chisel type. This is not used for
* hardware.
*/
private def apply_impl[T <: Data](source: => T, writable: Boolean): T = {
val prevId = Builder.idGen.value
val data = source // should only evaluate source once
requireIsChiselType(data)
val ret = if (!data.mustClone(prevId)) data else data.cloneType.asInstanceOf[T]
ret.probeInfo = ProbeInfo(true, writable)
ret
}

def apply[T <: Data](source: => T): T = apply_impl(source, false)

def writable[T <: Data](source: => T): T = apply_impl(source, true)

def requireIsProbe(probeExpr: Data): Unit = {
require(probeExpr.probeInfo.isProbe, s"expected $probeExpr to be a probe.")
}

def define(sink: Data, probeExpr: Data)(implicit sourceInfo: SourceInfo): Unit = {
requireIsProbe(probeExpr)
pushCommand(ProbeDefine(sourceInfo, sink.ref, probeExpr.ref))
}

def probe[T <: Data](source: => T): Data = {
val prevId = Builder.idGen.value
val t = source

// construct probe to return with cloned info
val clone = if (!t.mustClone(prevId)) t else t.cloneTypeFull
clone.bind(chisel3.internal.ProbeBinding(Builder.forcedUserModule, Builder.currentWhen, t))
clone.setRef(ProbeExpr(t.getRef))
clone.probeInfo = ProbeInfo(true, t.probeInfo.writable)

clone
}

def read[T <: Data](source: => T): Data = {
val prevId = Builder.idGen.value
val t = source
requireIsProbe(t)

// construct probe to return with cloned info
val clone = if (!t.mustClone(prevId)) t else t.cloneTypeFull
clone.bind(chisel3.internal.ProbeBinding(Builder.forcedUserModule, Builder.currentWhen, t))
clone.setRef(ProbeRead(t.getRef))

clone
}

def forceInitial(probe: Data, value: Data)(implicit sourceInfo: SourceInfo): Unit = {
requireIsProbe(probe)
pushCommand(ProbeForceInitial(sourceInfo, probe.ref, value.ref))
}

def releaseInitial(probe: Data)(implicit sourceInfo: SourceInfo): Unit = {
requireIsProbe(probe)
pushCommand(ProbeReleaseInitial(sourceInfo, probe.ref))
}

def force(clock: Clock, cond: Data, probe: Data, value: Data)(implicit sourceInfo: SourceInfo): Unit = {
requireIsProbe(probe)
pushCommand(ProbeForce(sourceInfo, clock.ref, cond.ref, probe.ref, value.ref))
}

def release(clock: Clock, cond: Data, probe: Data)(implicit sourceInfo: SourceInfo): Unit = {
requireIsProbe(probe)
pushCommand(ProbeRelease(sourceInfo, clock.ref, cond.ref, probe.ref))
}

}
4 changes: 4 additions & 0 deletions core/src/main/scala/chisel3/internal/Binding.scala
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ case class WireBinding(enclosure: RawModule, visibility: Option[WhenContext])
extends ConstrainedBinding
with ConditionalDeclarable

private[chisel3] case class ProbeBinding(enclosure: RawModule, visibility: Option[WhenContext], target: Data)
extends ConstrainedBinding
with ConditionalDeclarable

@deprecated(deprecatedPublicAPIMsg, "Chisel 3.6")
case class ChildBinding(parent: Data) extends Binding {
def location: Option[BaseModule] = parent.topBinding.location
Expand Down
42 changes: 41 additions & 1 deletion core/src/main/scala/chisel3/internal/firrtl/Converter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ 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 @ ProbeRead(probe) =>
fir.ProbeRead(convert(probe, ctx, info))
case other =>
throw new InternalErrorException(s"Unexpected type in convert $other")
}
Expand Down Expand Up @@ -177,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 @@ -302,7 +331,18 @@ private[chisel3] object Converter {

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

def extractType(data: Data, clearDir: Boolean, info: SourceInfo): fir.Type = data match {
def extractType(data: Data, clearDir: Boolean, info: SourceInfo, checkProbe: Boolean = true): fir.Type = {
if (checkProbe && data.probeInfo.isProbe) {
if (data.probeInfo.writable)
fir.RWProbeType(extractType(data, clearDir, info, false))
else
fir.ProbeType(extractType(data, clearDir, info, false))
} else {
extractTypeImpl(data, clearDir, info)
}
}

def extractTypeImpl(data: Data, clearDir: Boolean, info: SourceInfo): fir.Type = data match {
case _: Clock => fir.ClockType
case _: AsyncReset => fir.AsyncResetType
case _: ResetType => fir.ResetType
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}]"
}

private[chisel3] case class ProbeExpr(probe: Arg) extends Arg {
def name: String = s"$probe"
}

private[chisel3] case class ProbeRead(probe: Arg) extends Arg {
def name = s"$probe"
}

@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 @@ -339,6 +348,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
17 changes: 17 additions & 0 deletions firrtl/src/main/scala/firrtl/ir/IR.scala
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ abstract class Literal extends Expression {
val value: BigInt
val width: Width
}

case class UIntLiteral(value: BigInt, width: Width) extends Literal with UseSerializer {
def tpe = UIntType(width)
}
Expand Down Expand Up @@ -329,6 +330,19 @@ object Print {
}
}

case class ProbeDefine(info: Info, sink: Expression, probeExpr: Expression) extends Statement with UseSerializer
case class ProbeExpr(expr: Expression, tpe: Type = UnknownType) extends Expression with UseSerializer
case class ProbeRead(expr: Expression, tpe: Type = UnknownType) extends Expression with UseSerializer

case class ProbeForceInitial(info: Info, probe: Expression, value: Expression) extends Statement with UseSerializer
case class ProbeReleaseInitial(info: Info, probe: Expression) extends Statement with UseSerializer
case class ProbeForce(info: Info, clock: Expression, cond: Expression, probe: Expression, value: Expression)
extends Statement
with UseSerializer
case class ProbeRelease(info: Info, clock: Expression, cond: Expression, probe: Expression)
extends Statement
with UseSerializer

// formal
object Formal extends Enumeration {
val Assert = Value("assert")
Expand Down Expand Up @@ -445,6 +459,9 @@ object GroundType {
def unapply(ground: GroundType): Option[Width] = Some(ground.width)
}
abstract class AggregateType extends Type

case class ProbeType(underlying: Type) extends Type with UseSerializer
case class RWProbeType(underlying: Type) extends Type with UseSerializer
case class UIntType(width: Width) extends GroundType with UseSerializer
case class SIntType(width: Width) extends GroundType with UseSerializer

Expand Down
16 changes: 15 additions & 1 deletion firrtl/src/main/scala/firrtl/ir/Serializer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ object Serializer {
case ValidIf(cond, value, _) => b ++= "validif("; s(cond); b ++= ", "; s(value); b += ')'
case SIntLiteral(value, width) =>
b ++= "SInt"; s(width); b ++= "(\"h"; b ++= value.toString(16); b ++= "\")"
case other => b ++= other.serialize // Handle user-defined nodes
case ProbeExpr(expr, _) => b ++= "probe("; s(expr); b += ')'
case ProbeRead(expr, _) => b ++= "read("; s(expr); b += ')'
case other => b ++= other.serialize // Handle user-defined nodes
}

// Helper for some not-real Statements that only exist for Serialization
Expand Down Expand Up @@ -280,6 +282,16 @@ object Serializer {
case firrtl.CDefMPort(info, name, _, mem, exps, direction) =>
b ++= direction.serialize; b ++= " mport "; b ++= name; b ++= " = "; b ++= mem
b += '['; s(exps.head); b ++= "], "; s(exps(1)); s(info)
case ProbeDefine(info, sink, probeExpr) =>
b ++= "define "; s(sink); b ++= " = "; s(probeExpr); s(info)
case ProbeForceInitial(info, probe, value) =>
b ++= "force_initial("; s(probe); b ++= ", "; s(value); b += ')'; s(info)
case ProbeReleaseInitial(info, probe) =>
b ++= "release_initial("; s(probe); b += ')'; s(info)
case ProbeForce(info, clock, cond, probe, value) =>
b ++= "force("; s(clock); b ++= ", "; s(cond); b ++= ", "; s(probe); b ++= ", "; s(value); b += ')'; s(info)
case ProbeRelease(info, clock, cond, probe) =>
b ++= "release("; s(clock); b ++= ", "; s(cond); b ++= ", "; s(probe); b += ')'; s(info)
case other => b ++= other.serialize // Handle user-defined nodes
}

Expand Down Expand Up @@ -311,6 +323,8 @@ object Serializer {

private def s(node: Type)(implicit b: StringBuilder, indent: Int): Unit = node match {
// Types
case ProbeType(underlying: Type) => b ++= "Probe<"; s(underlying); b += '>'
case RWProbeType(underlying: Type) => b ++= "RWProbe<"; s(underlying); b += '>'
case UIntType(width: Width) => b ++= "UInt"; s(width)
case SIntType(width: Width) => b ++= "SInt"; s(width)
case BundleType(fields) => b ++= "{ "; sField(fields, ", "); b += '}'
Expand Down
36 changes: 36 additions & 0 deletions firrtl/src/test/scala/firrtlTests/SerializerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,42 @@ class SerializerSpec extends AnyFlatSpec with Matchers {
SMemTestCircuit.circuit(ReadUnderWrite.Old).serialize should include("old")
}

it should "support emitting Probe/RWProbe types and related expressions/statements" in {
val probeInt = DefWire(NoInfo, "foo", ProbeType(UIntType(IntWidth(3))))
Serializer.serialize(probeInt) should be("wire foo : Probe<UInt<3>>")

val rwProbeBundle = DefWire(
NoInfo,
"foo",
RWProbeType(
BundleType(
Seq(
Field("bar", Default, UIntType(IntWidth(32)))
)
)
)
)
Serializer.serialize(rwProbeBundle) should be("wire foo : RWProbe<{ bar : UInt<32>}>")

val probeDefine = ProbeDefine(NoInfo, SubField(Reference("c"), "in"), ProbeExpr(Reference("in")))
Serializer.serialize(probeDefine) should be("define c.in = probe(in)")

val probeRead = Connect(NoInfo, Reference("out"), ProbeRead(Reference("c.out")))
Serializer.serialize(probeRead) should be("out <= read(c.out)")

val probeForceInitial = ProbeForceInitial(NoInfo, Reference("outProbe"), UIntLiteral(100, IntWidth(8)))
Serializer.serialize(probeForceInitial) should be("force_initial(outProbe, UInt<8>(\"h64\"))")

val probeReleaseInitial = ProbeReleaseInitial(NoInfo, Reference("outProbe"))
Serializer.serialize(probeReleaseInitial) should be("release_initial(outProbe)")

val probeForce = ProbeForce(NoInfo, Reference("clock"), Reference("cond"), Reference("outProbe"), Reference("in"))
Serializer.serialize(probeForce) should be("force(clock, cond, outProbe, in)")

val probeRelease = ProbeRelease(NoInfo, Reference("clock"), Reference("cond"), Reference("outProbe"))
Serializer.serialize(probeRelease) should be("release(clock, cond, outProbe)")
}

it should "support lazy serialization" in {
var stmtSerialized = false
case class HackStmt(stmt: Statement) extends Statement {
Expand Down
Loading