From 7370d9ba2fe7cca00a4869ea62f73c029b0c3518 Mon Sep 17 00:00:00 2001 From: Casper Cromjongh Date: Tue, 3 Sep 2024 19:28:49 +0200 Subject: [PATCH 1/6] Attempt to add the strong type check for all connections. Multiple overloaded methods cannot define default arguments, however. --- .../nl/tudelft/tydi_chisel/TydiLib.scala | 51 +++++++++---------- .../tydi_chisel/StreamCompatCheckTest.scala | 14 +++-- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala b/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala index 72f80f7..f033e3f 100644 --- a/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala +++ b/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala @@ -385,6 +385,23 @@ abstract class PhysicalStreamBase(private val e: TydiEl, val n: Int, val d: Int, } } + def elementCheckTyped(toConnect: PhysicalStreamBase, typeCheck: CompatCheck.Value): Unit = { + if (typeCheck == CompatCheck.Strict) { + if (this.getDataType.getClass != toConnect.getDataType.getClass) { + reportProblem( + s"Type of stream elements is not equal. ${this} has e=${this.getDataType.getClass}, ${toConnect} has e=${toConnect.getDataType.getClass}" + ) + } + if (this.user.getClass != toConnect.user.getClass) { + reportProblem( + s"Type of user elements is not equal. ${this} has u=${this.getUserType.getClass}, ${toConnect} has u=${toConnect.getUserType.getClass}" + ) + } + } else { + elementCheck(toConnect) + } + } + /** * Meta-connect function. Connects all metadata signals but not the data or user signals. * @param bundle Source stream to drive this stream with. @@ -463,9 +480,11 @@ class PhysicalStream(private val e: TydiEl, n: Int = 1, d: Int = 1, c: Int, priv * @tparam Tel Element signal type. * @tparam Tus User signal type. */ - def :=[Tel <: TydiEl, Tus <: Data](bundle: PhysicalStreamDetailed[Tel, Tus]): Unit = { + def :=[Tel <: TydiEl, Tus <: Data]( + bundle: PhysicalStreamDetailed[Tel, Tus] + )(implicit typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { this :~= bundle - elementCheck(bundle) + elementCheckTyped(bundle, typeCheck) if (elWidth > 0) { this.data := bundle.getDataConcat } else { @@ -482,9 +501,9 @@ class PhysicalStream(private val e: TydiEl, n: Int = 1, d: Int = 1, c: Int, priv * Stream mounting function. * @param bundle Source stream to drive this stream with. */ - def :=(bundle: PhysicalStream): Unit = { + def :=(bundle: PhysicalStream)(implicit typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { this :~= bundle - elementCheck(bundle) + elementCheckTyped(bundle, typeCheck) this.data := bundle.data this.user := bundle.user } @@ -606,26 +625,6 @@ class PhysicalStreamDetailed[Tel <: TydiEl, Tus <: Data]( io } - def elementCheckTyped[TBel <: TydiEl, TBus <: Data]( - toConnect: PhysicalStreamDetailed[TBel, TBus], - typeCheck: CompatCheck.Value - ): Unit = { - if (typeCheck == CompatCheck.Strict) { - if (this.getDataType.getClass != toConnect.getDataType.getClass) { - reportProblem( - s"Type of stream elements is not equal. ${this} has e=${this.getDataType.getClass}, ${toConnect} has e=${toConnect.getDataType.getClass}" - ) - } - if (this.user.getClass != toConnect.user.getClass) { - reportProblem( - s"Type of user elements is not equal. ${this} has u=${this.getUserType.getClass}, ${toConnect} has u=${toConnect.getUserType.getClass}" - ) - } - } else { - super.elementCheck(toConnect) - } - } - // Require the element and user signal types to be the same as this stream in the function signature. /** * Stream mounting function. @@ -679,9 +678,9 @@ class PhysicalStreamDetailed[Tel <: TydiEl, Tus <: Data]( * Stream mounting function. * @param bundle Source stream to drive this stream with. */ - def :=(bundle: PhysicalStream): Unit = { + def :=(bundle: PhysicalStream)(implicit typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { paramCheck(bundle) - bundle.elementCheck(this) + elementCheckTyped(bundle, typeCheck) // We cannot use the :~= function here since the last vector must be driven by the bitvector this.endi := bundle.endi this.stai := bundle.stai diff --git a/testing/src/test/scala/nl/tudelft/tydi_chisel/StreamCompatCheckTest.scala b/testing/src/test/scala/nl/tudelft/tydi_chisel/StreamCompatCheckTest.scala index be8328d..06e3870 100644 --- a/testing/src/test/scala/nl/tudelft/tydi_chisel/StreamCompatCheckTest.scala +++ b/testing/src/test/scala/nl/tudelft/tydi_chisel/StreamCompatCheckTest.scala @@ -15,12 +15,17 @@ class StreamCompatCheckTest extends AnyFlatSpec with ChiselScalatestTester { class StreamConnectMod( in: PhysicalStream, out: PhysicalStream, + typeCheckSelect: CompatCheck.Value = CompatCheck.Strict, errorReporting: CompatCheckResult.Value = CompatCheckResult.Error ) extends TydiModule { nl.tudelft.tydi_chisel setCompatCheckResult errorReporting val inStream: PhysicalStream = IO(Flipped(in)) val outStream: PhysicalStream = IO(out) - outStream := inStream + + { + implicit val typeCheckImplicit: CompatCheck.Value = typeCheckSelect + outStream := inStream + } } class DetailedStreamConnectMod[TIel <: TydiEl, TIus <: Data, TOel <: TydiEl, TOus <: Data]( @@ -32,7 +37,6 @@ class StreamCompatCheckTest extends AnyFlatSpec with ChiselScalatestTester { val outStream: PhysicalStreamDetailed[TOel, TOus] = IO(out) { - // Value gets correctly overridden implicit val typeCheckImplicit: CompatCheck.Value = typeCheckSelect outStream := inStream } @@ -53,10 +57,14 @@ class StreamCompatCheckTest extends AnyFlatSpec with ChiselScalatestTester { intercept[TydiStreamCompatException] { test(new DetailedStreamConnectMod(myBundleStream, myBundle2Stream)) { _ => } } + intercept[TydiStreamCompatException] { + test(new StreamConnectMod(PhysicalStream(new MyBundle, c = 1), PhysicalStream(new MyBundle2, c = 1))) { _ => } + } } it should "weak check type" in { test(new DetailedStreamConnectMod(myBundleStream, myBundle2Stream, CompatCheck.Params)) { _ => } + test(new StreamConnectMod(PhysicalStream(new MyBundle, c = 1), PhysicalStream(new MyBundle2, c = 1))) { _ => } } it should "check parameters" in { @@ -92,7 +100,7 @@ class StreamCompatCheckTest extends AnyFlatSpec with ChiselScalatestTester { new StreamConnectMod( baseStream, PhysicalStream(new MyBundle, n = 2, d = 1, c = 1, new DataBundle), - CompatCheckResult.Warning + errorReporting=CompatCheckResult.Warning ) ) { _ => } } From 82214a3c2b90fa2ff2f240905061c982a1a67a44 Mon Sep 17 00:00:00 2001 From: Casper Cromjongh Date: Tue, 3 Sep 2024 19:51:24 +0200 Subject: [PATCH 2/6] Avoid overloaded method issues by upcasting. This has the issue that type parameters are not checked. --- .../nl/tudelft/tydi_chisel/TydiLib.scala | 28 ++++++++++++------- .../tydi_chisel/StreamCompatCheckTest.scala | 2 +- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala b/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala index f033e3f..e052cb5 100644 --- a/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala +++ b/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala @@ -217,7 +217,7 @@ final case class TydiStreamCompatException(private val message: String = "", pri * @param c Complexity * @param u User signals */ -abstract class PhysicalStreamBase(private val e: TydiEl, val n: Int, val d: Int, val c: Int, private val u: Data) +sealed abstract class PhysicalStreamBase(private val e: TydiEl, val n: Int, val d: Int, val c: Int, private val u: Data) extends TydiEl { override val isStream: Boolean = true override val elWidth: Int = e.getDataElementsRec.map(_.getWidth).sum @@ -392,7 +392,7 @@ abstract class PhysicalStreamBase(private val e: TydiEl, val n: Int, val d: Int, s"Type of stream elements is not equal. ${this} has e=${this.getDataType.getClass}, ${toConnect} has e=${toConnect.getDataType.getClass}" ) } - if (this.user.getClass != toConnect.user.getClass) { + if (this.getUserType.getClass != toConnect.getUserType.getClass) { reportProblem( s"Type of user elements is not equal. ${this} has u=${this.getUserType.getClass}, ${toConnect} has u=${toConnect.getUserType.getClass}" ) @@ -426,6 +426,16 @@ abstract class PhysicalStreamBase(private val e: TydiEl, val n: Int, val d: Int, } } + def :=[TBel <: TydiEl, TBus <: Data](bundle: PhysicalStreamBase)(implicit typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { + // TBel and TBus checking is not possible because of erasure. + (this, bundle) match { + case (x: PhysicalStream, y: PhysicalStream) => x.connectSimple(y, typeCheck) + case (x: PhysicalStream, y: PhysicalStreamDetailed[TBel, TBus]) => x.connectDetailed(y, typeCheck) + case (x: PhysicalStreamDetailed[TBel, TBus], y: PhysicalStream) => x.connectSimple(y, typeCheck) + case (x: PhysicalStreamDetailed[TBel, TBus], y: PhysicalStreamDetailed[TBel, TBus]) => x.connectDetailed(y, typeCheck) + } + } + def tydiCode: String = { val elName = e.fingerprint val usName = u.fingerprint @@ -480,9 +490,8 @@ class PhysicalStream(private val e: TydiEl, n: Int = 1, d: Int = 1, c: Int, priv * @tparam Tel Element signal type. * @tparam Tus User signal type. */ - def :=[Tel <: TydiEl, Tus <: Data]( - bundle: PhysicalStreamDetailed[Tel, Tus] - )(implicit typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { + private[tydi_chisel] def connectDetailed[Tel <: TydiEl, Tus <: Data]( + bundle: PhysicalStreamDetailed[Tel, Tus], typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { this :~= bundle elementCheckTyped(bundle, typeCheck) if (elWidth > 0) { @@ -501,7 +510,7 @@ class PhysicalStream(private val e: TydiEl, n: Int = 1, d: Int = 1, c: Int, priv * Stream mounting function. * @param bundle Source stream to drive this stream with. */ - def :=(bundle: PhysicalStream)(implicit typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { + private[tydi_chisel] def connectSimple(bundle: PhysicalStream, typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { this :~= bundle elementCheckTyped(bundle, typeCheck) this.data := bundle.data @@ -630,9 +639,8 @@ class PhysicalStreamDetailed[Tel <: TydiEl, Tus <: Data]( * Stream mounting function. * @param bundle Source stream to drive this stream with. */ - def :=[TBel <: TydiEl, TBus <: Data]( - bundle: PhysicalStreamDetailed[TBel, TBus] - )(implicit typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { + private[tydi_chisel] def connectDetailed[TBel <: TydiEl, TBus <: Data]( + bundle: PhysicalStreamDetailed[TBel, TBus], typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { elementCheckTyped(bundle, typeCheck) // This could be done with a :<>= but I like being explicit here to catch possible errors. if (bundle.r && !this.r) { @@ -678,7 +686,7 @@ class PhysicalStreamDetailed[Tel <: TydiEl, Tus <: Data]( * Stream mounting function. * @param bundle Source stream to drive this stream with. */ - def :=(bundle: PhysicalStream)(implicit typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { + private[tydi_chisel] def connectSimple(bundle: PhysicalStream, typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { paramCheck(bundle) elementCheckTyped(bundle, typeCheck) // We cannot use the :~= function here since the last vector must be driven by the bitvector diff --git a/testing/src/test/scala/nl/tudelft/tydi_chisel/StreamCompatCheckTest.scala b/testing/src/test/scala/nl/tudelft/tydi_chisel/StreamCompatCheckTest.scala index 06e3870..43ceda3 100644 --- a/testing/src/test/scala/nl/tudelft/tydi_chisel/StreamCompatCheckTest.scala +++ b/testing/src/test/scala/nl/tudelft/tydi_chisel/StreamCompatCheckTest.scala @@ -64,7 +64,7 @@ class StreamCompatCheckTest extends AnyFlatSpec with ChiselScalatestTester { it should "weak check type" in { test(new DetailedStreamConnectMod(myBundleStream, myBundle2Stream, CompatCheck.Params)) { _ => } - test(new StreamConnectMod(PhysicalStream(new MyBundle, c = 1), PhysicalStream(new MyBundle2, c = 1))) { _ => } + test(new StreamConnectMod(PhysicalStream(new MyBundle, c = 1), PhysicalStream(new MyBundle2, c = 1), CompatCheck.Params)) { _ => } } it should "check parameters" in { From 6d691c6debc3a5a13b2adf4dfcb24d898bef67c6 Mon Sep 17 00:00:00 2001 From: Casper Cromjongh Date: Tue, 3 Sep 2024 19:52:48 +0200 Subject: [PATCH 3/6] Format. --- .../nl/tudelft/tydi_chisel/TydiLib.scala | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala b/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala index e052cb5..06f8e54 100644 --- a/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala +++ b/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala @@ -426,13 +426,12 @@ sealed abstract class PhysicalStreamBase(private val e: TydiEl, val n: Int, val } } - def :=[TBel <: TydiEl, TBus <: Data](bundle: PhysicalStreamBase)(implicit typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { - // TBel and TBus checking is not possible because of erasure. + def :=(bundle: PhysicalStreamBase)(implicit typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { (this, bundle) match { - case (x: PhysicalStream, y: PhysicalStream) => x.connectSimple(y, typeCheck) - case (x: PhysicalStream, y: PhysicalStreamDetailed[TBel, TBus]) => x.connectDetailed(y, typeCheck) - case (x: PhysicalStreamDetailed[TBel, TBus], y: PhysicalStream) => x.connectSimple(y, typeCheck) - case (x: PhysicalStreamDetailed[TBel, TBus], y: PhysicalStreamDetailed[TBel, TBus]) => x.connectDetailed(y, typeCheck) + case (x: PhysicalStream, y: PhysicalStream) => x.connectSimple(y, typeCheck) + case (x: PhysicalStream, y: PhysicalStreamDetailed[_, _]) => x.connectDetailed(y, typeCheck) + case (x: PhysicalStreamDetailed[_, _], y: PhysicalStream) => x.connectSimple(y, typeCheck) + case (x: PhysicalStreamDetailed[_, _], y: PhysicalStreamDetailed[_, _]) => x.connectDetailed(y, typeCheck) } } @@ -491,7 +490,9 @@ class PhysicalStream(private val e: TydiEl, n: Int = 1, d: Int = 1, c: Int, priv * @tparam Tus User signal type. */ private[tydi_chisel] def connectDetailed[Tel <: TydiEl, Tus <: Data]( - bundle: PhysicalStreamDetailed[Tel, Tus], typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { + bundle: PhysicalStreamDetailed[Tel, Tus], + typeCheck: CompatCheck.Value = CompatCheck.Strict + ): Unit = { this :~= bundle elementCheckTyped(bundle, typeCheck) if (elWidth > 0) { @@ -510,7 +511,10 @@ class PhysicalStream(private val e: TydiEl, n: Int = 1, d: Int = 1, c: Int, priv * Stream mounting function. * @param bundle Source stream to drive this stream with. */ - private[tydi_chisel] def connectSimple(bundle: PhysicalStream, typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { + private[tydi_chisel] def connectSimple( + bundle: PhysicalStream, + typeCheck: CompatCheck.Value = CompatCheck.Strict + ): Unit = { this :~= bundle elementCheckTyped(bundle, typeCheck) this.data := bundle.data @@ -640,7 +644,9 @@ class PhysicalStreamDetailed[Tel <: TydiEl, Tus <: Data]( * @param bundle Source stream to drive this stream with. */ private[tydi_chisel] def connectDetailed[TBel <: TydiEl, TBus <: Data]( - bundle: PhysicalStreamDetailed[TBel, TBus], typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { + bundle: PhysicalStreamDetailed[TBel, TBus], + typeCheck: CompatCheck.Value = CompatCheck.Strict + ): Unit = { elementCheckTyped(bundle, typeCheck) // This could be done with a :<>= but I like being explicit here to catch possible errors. if (bundle.r && !this.r) { @@ -686,7 +692,10 @@ class PhysicalStreamDetailed[Tel <: TydiEl, Tus <: Data]( * Stream mounting function. * @param bundle Source stream to drive this stream with. */ - private[tydi_chisel] def connectSimple(bundle: PhysicalStream, typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { + private[tydi_chisel] def connectSimple( + bundle: PhysicalStream, + typeCheck: CompatCheck.Value = CompatCheck.Strict + ): Unit = { paramCheck(bundle) elementCheckTyped(bundle, typeCheck) // We cannot use the :~= function here since the last vector must be driven by the bitvector From 6a587c3243e31155ab5551ed220af13dc253bf33 Mon Sep 17 00:00:00 2001 From: Casper Cromjongh Date: Tue, 3 Sep 2024 20:08:14 +0200 Subject: [PATCH 4/6] Attempt to convert check result back to implicit. --- .../nl/tudelft/tydi_chisel/TydiLib.scala | 83 ++++++++++++------- .../nl/tudelft/tydi_chisel/package.scala | 8 -- .../tydi_chisel/StreamCompatCheckTest.scala | 2 +- 3 files changed, 55 insertions(+), 38 deletions(-) diff --git a/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala b/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala index 06f8e54..43f99e2 100644 --- a/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala +++ b/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala @@ -343,7 +343,7 @@ sealed abstract class PhysicalStreamBase(private val e: TydiEl, val n: Int, val Console.err.println(s"$bold$orange$message$reset") } - protected def reportProblem(problemStr: String): Unit = { + protected def reportProblem(problemStr: String, compatCheckResult: CompatCheckResult.Value): Unit = { compatCheckResult match { case CompatCheckResult.Error => throw TydiStreamCompatException(problemStr) case CompatCheckResult.Warning => printWarning(problemStr) @@ -354,51 +354,66 @@ sealed abstract class PhysicalStreamBase(private val e: TydiEl, val n: Int, val * Check if the parameters of a source and sink stream match. * @param toConnect Source stream to drive this stream with. */ - def paramCheck(toConnect: PhysicalStreamBase): Unit = { + def paramCheck(toConnect: PhysicalStreamBase, compatCheckResult: CompatCheckResult.Value): Unit = { // Number of lanes should be the same if (toConnect.n != this.n) { - reportProblem(s"Number of lanes between source and sink is not equal. ${this} has n=${this.n}, ${toConnect - .toString()} has n=${toConnect.n}") + reportProblem( + s"Number of lanes between source and sink is not equal. ${this} has n=${this.n}, ${toConnect + .toString()} has n=${toConnect.n}", + compatCheckResult + ) } // Dimensionality should be the same if (toConnect.d != this.d) { reportProblem( - s"Dimensionality of source and sink is not equal. ${this} has d=${this.d}, ${toConnect} has d=${toConnect.d}" + s"Dimensionality of source and sink is not equal. ${this} has d=${this.d}, ${toConnect} has d=${toConnect.d}", + compatCheckResult ) } // Sink C >= source C for compatibility if (toConnect.c > this.c) { - reportProblem(s"Complexity of source stream > sink. ${this} has c=${this.c}, ${toConnect} has c=${toConnect.c}") + reportProblem( + s"Complexity of source stream > sink. ${this} has c=${this.c}, ${toConnect} has c=${toConnect.c}", + compatCheckResult + ) } } - def elementCheck(toConnect: PhysicalStreamBase): Unit = { + def elementCheck(toConnect: PhysicalStreamBase, compatCheckResult: CompatCheckResult.Value): Unit = { if (this.elWidth != toConnect.elWidth) { reportProblem( - s"Size of stream elements is not equal. ${this} has |e|=${this.elWidth}, ${toConnect} has |e|=${toConnect.elWidth}" + s"Size of stream elements is not equal. ${this} has |e|=${this.elWidth}, ${toConnect} has |e|=${toConnect.elWidth}", + compatCheckResult ) } if (this.userElWidth != toConnect.userElWidth) { reportProblem( - s"Size of stream elements is not equal. ${this} has |u|=${this.userElWidth}, ${toConnect} has |u|=${toConnect.userElWidth}" + s"Size of stream elements is not equal. ${this} has |u|=${this.userElWidth}, ${toConnect} has |u|=${toConnect.userElWidth}", + compatCheckResult ) } } - def elementCheckTyped(toConnect: PhysicalStreamBase, typeCheck: CompatCheck.Value): Unit = { + def elementCheckTyped( + toConnect: PhysicalStreamBase, + typeCheck: CompatCheck.Value, + compatCheckResult: CompatCheckResult.Value + ): Unit = { if (typeCheck == CompatCheck.Strict) { if (this.getDataType.getClass != toConnect.getDataType.getClass) { reportProblem( - s"Type of stream elements is not equal. ${this} has e=${this.getDataType.getClass}, ${toConnect} has e=${toConnect.getDataType.getClass}" + s"Type of stream elements is not equal. ${this} has e=${this.getDataType.getClass}, ${toConnect} has e=${toConnect.getDataType.getClass}", + compatCheckResult ) } if (this.getUserType.getClass != toConnect.getUserType.getClass) { reportProblem( - s"Type of user elements is not equal. ${this} has u=${this.getUserType.getClass}, ${toConnect} has u=${toConnect.getUserType.getClass}" + s"Type of user elements is not equal. ${this} has u=${this.getUserType.getClass}, ${toConnect} has u=${toConnect.getUserType.getClass}", + compatCheckResult ) } } else { - elementCheck(toConnect) + elementCheck(toConnect, compatCheckResult) } } @@ -406,8 +421,10 @@ sealed abstract class PhysicalStreamBase(private val e: TydiEl, val n: Int, val * Meta-connect function. Connects all metadata signals but not the data or user signals. * @param bundle Source stream to drive this stream with. */ - def :~=(bundle: PhysicalStreamBase): Unit = { - paramCheck(bundle) + def :~=( + bundle: PhysicalStreamBase + )(implicit compatCheckResult: CompatCheckResult.Value = CompatCheckResult.Error): Unit = { + paramCheck(bundle, compatCheckResult) // This could be done with a :<>= but I like being explicit here to catch possible errors. this.endi := bundle.endi this.stai := bundle.stai @@ -426,12 +443,16 @@ sealed abstract class PhysicalStreamBase(private val e: TydiEl, val n: Int, val } } - def :=(bundle: PhysicalStreamBase)(implicit typeCheck: CompatCheck.Value = CompatCheck.Strict): Unit = { + def :=(bundle: PhysicalStreamBase)(implicit + typeCheck: CompatCheck.Value = CompatCheck.Strict, + errorReporting: CompatCheckResult.Value = CompatCheckResult.Error + ): Unit = { (this, bundle) match { - case (x: PhysicalStream, y: PhysicalStream) => x.connectSimple(y, typeCheck) - case (x: PhysicalStream, y: PhysicalStreamDetailed[_, _]) => x.connectDetailed(y, typeCheck) - case (x: PhysicalStreamDetailed[_, _], y: PhysicalStream) => x.connectSimple(y, typeCheck) - case (x: PhysicalStreamDetailed[_, _], y: PhysicalStreamDetailed[_, _]) => x.connectDetailed(y, typeCheck) + case (x: PhysicalStream, y: PhysicalStream) => x.connectSimple(y, typeCheck, errorReporting) + case (x: PhysicalStream, y: PhysicalStreamDetailed[_, _]) => x.connectDetailed(y, typeCheck, errorReporting) + case (x: PhysicalStreamDetailed[_, _], y: PhysicalStream) => x.connectSimple(y, typeCheck, errorReporting) + case (x: PhysicalStreamDetailed[_, _], y: PhysicalStreamDetailed[_, _]) => + x.connectDetailed(y, typeCheck, errorReporting) } } @@ -491,10 +512,11 @@ class PhysicalStream(private val e: TydiEl, n: Int = 1, d: Int = 1, c: Int, priv */ private[tydi_chisel] def connectDetailed[Tel <: TydiEl, Tus <: Data]( bundle: PhysicalStreamDetailed[Tel, Tus], - typeCheck: CompatCheck.Value = CompatCheck.Strict + typeCheck: CompatCheck.Value = CompatCheck.Strict, + errorReporting: CompatCheckResult.Value ): Unit = { this :~= bundle - elementCheckTyped(bundle, typeCheck) + elementCheckTyped(bundle, typeCheck, errorReporting) if (elWidth > 0) { this.data := bundle.getDataConcat } else { @@ -513,10 +535,11 @@ class PhysicalStream(private val e: TydiEl, n: Int = 1, d: Int = 1, c: Int, priv */ private[tydi_chisel] def connectSimple( bundle: PhysicalStream, - typeCheck: CompatCheck.Value = CompatCheck.Strict + typeCheck: CompatCheck.Value, + errorReporting: CompatCheckResult.Value ): Unit = { this :~= bundle - elementCheckTyped(bundle, typeCheck) + elementCheckTyped(bundle, typeCheck, errorReporting) this.data := bundle.data this.user := bundle.user } @@ -645,9 +668,10 @@ class PhysicalStreamDetailed[Tel <: TydiEl, Tus <: Data]( */ private[tydi_chisel] def connectDetailed[TBel <: TydiEl, TBus <: Data]( bundle: PhysicalStreamDetailed[TBel, TBus], - typeCheck: CompatCheck.Value = CompatCheck.Strict + typeCheck: CompatCheck.Value = CompatCheck.Strict, + errorReporting: CompatCheckResult.Value ): Unit = { - elementCheckTyped(bundle, typeCheck) + elementCheckTyped(bundle, typeCheck, errorReporting) // This could be done with a :<>= but I like being explicit here to catch possible errors. if (bundle.r && !this.r) { this :~= bundle @@ -694,10 +718,11 @@ class PhysicalStreamDetailed[Tel <: TydiEl, Tus <: Data]( */ private[tydi_chisel] def connectSimple( bundle: PhysicalStream, - typeCheck: CompatCheck.Value = CompatCheck.Strict + typeCheck: CompatCheck.Value = CompatCheck.Strict, + errorReporting: CompatCheckResult.Value ): Unit = { - paramCheck(bundle) - elementCheckTyped(bundle, typeCheck) + paramCheck(bundle, errorReporting) + elementCheckTyped(bundle, typeCheck, errorReporting) // We cannot use the :~= function here since the last vector must be driven by the bitvector this.endi := bundle.endi this.stai := bundle.stai diff --git a/library/src/main/scala/nl/tudelft/tydi_chisel/package.scala b/library/src/main/scala/nl/tudelft/tydi_chisel/package.scala index b344d64..ad06d5b 100644 --- a/library/src/main/scala/nl/tudelft/tydi_chisel/package.scala +++ b/library/src/main/scala/nl/tudelft/tydi_chisel/package.scala @@ -4,12 +4,4 @@ package object tydi_chisel { val firNoOptimizationOpts: Array[String] = Array("-disable-opt", "-disable-all-randomization", "-strip-debug-info") val firNormalOpts: Array[String] = Array("-O=debug", "-disable-all-randomization", "-strip-debug-info") val firReleaseOpts: Array[String] = Array("-O=release", "-disable-all-randomization", "-strip-debug-info") - - private[this] var _compatCheckResult: CompatCheckResult.Value = CompatCheckResult.Error - - def compatCheckResult: CompatCheckResult.Value = _compatCheckResult - - def setCompatCheckResult(value: CompatCheckResult.Value): Unit = { - _compatCheckResult = value - } } diff --git a/testing/src/test/scala/nl/tudelft/tydi_chisel/StreamCompatCheckTest.scala b/testing/src/test/scala/nl/tudelft/tydi_chisel/StreamCompatCheckTest.scala index 43ceda3..28d2dc2 100644 --- a/testing/src/test/scala/nl/tudelft/tydi_chisel/StreamCompatCheckTest.scala +++ b/testing/src/test/scala/nl/tudelft/tydi_chisel/StreamCompatCheckTest.scala @@ -18,12 +18,12 @@ class StreamCompatCheckTest extends AnyFlatSpec with ChiselScalatestTester { typeCheckSelect: CompatCheck.Value = CompatCheck.Strict, errorReporting: CompatCheckResult.Value = CompatCheckResult.Error ) extends TydiModule { - nl.tudelft.tydi_chisel setCompatCheckResult errorReporting val inStream: PhysicalStream = IO(Flipped(in)) val outStream: PhysicalStream = IO(out) { implicit val typeCheckImplicit: CompatCheck.Value = typeCheckSelect + implicit val typeCheckResultImplicit: CompatCheckResult.Value = errorReporting outStream := inStream } } From b1f9666983bf270af59c26ff5408df4e6ce5a089 Mon Sep 17 00:00:00 2001 From: Casper Cromjongh Date: Mon, 9 Sep 2024 16:08:17 +0200 Subject: [PATCH 5/6] Lift meta connection and type check to common function call. --- .../nl/tudelft/tydi_chisel/TydiLib.scala | 83 ++++++------------- 1 file changed, 25 insertions(+), 58 deletions(-) diff --git a/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala b/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala index 43f99e2..6497c2d 100644 --- a/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala +++ b/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala @@ -436,6 +436,11 @@ sealed abstract class PhysicalStreamBase(private val e: TydiEl, val n: Int, val (this.last, bundle.last) match { case (_: UInt, _: UInt) | (_: Vec[_], _: Vec[_]) => this.last := bundle.last case (_: UInt, _: Vec[_]) => this.last := bundle.last.asUInt + case (thisLast: Vec[_], bundleLast: UInt) => + for ((lastLane, i) <- thisLast.zipWithIndex) { + // Assign a slice of the bitvector to the respective lane in the vector + lastLane := bundleLast((i + 1) * d - 1, i * d) + } } } else { this.last := DontCare @@ -447,6 +452,8 @@ sealed abstract class PhysicalStreamBase(private val e: TydiEl, val n: Int, val typeCheck: CompatCheck.Value = CompatCheck.Strict, errorReporting: CompatCheckResult.Value = CompatCheckResult.Error ): Unit = { + this :~= bundle + elementCheckTyped(bundle, typeCheck, errorReporting) (this, bundle) match { case (x: PhysicalStream, y: PhysicalStream) => x.connectSimple(y, typeCheck, errorReporting) case (x: PhysicalStream, y: PhysicalStreamDetailed[_, _]) => x.connectDetailed(y, typeCheck, errorReporting) @@ -515,8 +522,6 @@ class PhysicalStream(private val e: TydiEl, n: Int = 1, d: Int = 1, c: Int, priv typeCheck: CompatCheck.Value = CompatCheck.Strict, errorReporting: CompatCheckResult.Value ): Unit = { - this :~= bundle - elementCheckTyped(bundle, typeCheck, errorReporting) if (elWidth > 0) { this.data := bundle.getDataConcat } else { @@ -538,8 +543,6 @@ class PhysicalStream(private val e: TydiEl, n: Int = 1, d: Int = 1, c: Int, priv typeCheck: CompatCheck.Value, errorReporting: CompatCheckResult.Value ): Unit = { - this :~= bundle - elementCheckTyped(bundle, typeCheck, errorReporting) this.data := bundle.data this.user := bundle.user } @@ -671,45 +674,25 @@ class PhysicalStreamDetailed[Tel <: TydiEl, Tus <: Data]( typeCheck: CompatCheck.Value = CompatCheck.Strict, errorReporting: CompatCheckResult.Value ): Unit = { - elementCheckTyped(bundle, typeCheck, errorReporting) - // This could be done with a :<>= but I like being explicit here to catch possible errors. - if (bundle.r && !this.r) { - this :~= bundle - // The following would work if we would know with certainty that the signals are oriented the right way, - // but we do not -.- - // (this.data: Data) :<>= (bundle.data: Data) - - // Using the recursive function leads to duplicate connections when connecting sub-streams, but the non-recursive - // version cannot be used, since non-stream elements could still contain stream items. - for ((thisData, bundleData) <- this.getDataElementsRec.zip(bundle.getDataElementsRec)) { - thisData :<>= bundleData - } - for ( - (thisStream: PhysicalStreamDetailed[_, _], bundleStream: PhysicalStreamDetailed[_, _]) <- this.getStreamElements - .zip(bundle.getStreamElements) - ) { - thisStream := bundleStream - } - (this.user: Data) :<>= (bundle.user: Data) - } else { - bundle :~= this - // The following would work if we would know with certainty that the signals are oriented the right way, - // but we do not -.- - // (bundle.data: Data) :<>= (this.data: Data) - - // Using the recursive function leads to duplicate connections when connecting sub-streams, but the non-recursive - // version cannot be used, since non-stream elements could still contain stream items. - for ((thisData, bundleData) <- this.getDataElementsRec.zip(bundle.getDataElementsRec)) { - bundleData :<>= thisData - } - for ( - (thisStream: PhysicalStreamDetailed[_, _], bundleStream: PhysicalStreamDetailed[_, _]) <- this.getStreamElements - .zip(bundle.getStreamElements) - ) { - bundleStream := thisStream - } - (bundle.user: Data) :<>= (this.user: Data) + if (!bundle.r || this.r) { + throw new Exception("Attempting to connect an input to an output. Reverse the connection.") + } + // The following would work if we would know with certainty that the signals are oriented the right way, + // but we do not -.- + // (this.data: Data) :<>= (bundle.data: Data) + + // Using the recursive function leads to duplicate connections when connecting sub-streams, but the non-recursive + // version cannot be used, since non-stream elements could still contain stream items. + for ((thisData, bundleData) <- this.getDataElementsRec.zip(bundle.getDataElementsRec)) { + thisData :<>= bundleData } + for ( + (thisStream: PhysicalStreamDetailed[_, _], bundleStream: PhysicalStreamDetailed[_, _]) <- this.getStreamElements + .zip(bundle.getStreamElements) + ) { + thisStream := bundleStream + } + (this.user: Data) :<>= (bundle.user: Data) } /** @@ -721,22 +704,6 @@ class PhysicalStreamDetailed[Tel <: TydiEl, Tus <: Data]( typeCheck: CompatCheck.Value = CompatCheck.Strict, errorReporting: CompatCheckResult.Value ): Unit = { - paramCheck(bundle, errorReporting) - elementCheckTyped(bundle, typeCheck, errorReporting) - // We cannot use the :~= function here since the last vector must be driven by the bitvector - this.endi := bundle.endi - this.stai := bundle.stai - this.strb := bundle.strb - // There are only last bits if there is dimensionality - if (d > 0) { - for ((lastLane, i) <- this.last.zipWithIndex) { - lastLane := bundle.last((i + 1) * d - 1, i * d) - } - } else { - this.last := DontCare - } - this.valid := bundle.valid - bundle.ready := this.ready // Connect data bitvector back to bundle for ((dataLane, i) <- this.data.zipWithIndex) { val dataWidth = bundle.elWidth From ec48f85f0a8e29540558722eb39acbbefe961cf7 Mon Sep 17 00:00:00 2001 From: Casper Cromjongh Date: Mon, 9 Sep 2024 16:22:16 +0200 Subject: [PATCH 6/6] Finishing touches to connect functions. Run formatting. --- .../nl/tudelft/tydi_chisel/TydiLib.scala | 27 ++++++++++++++++--- .../tydi_chisel/StreamCompatCheckTest.scala | 12 ++++++--- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala b/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala index 6497c2d..b4ae148 100644 --- a/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala +++ b/library/src/main/scala/nl/tudelft/tydi_chisel/TydiLib.scala @@ -420,8 +420,9 @@ sealed abstract class PhysicalStreamBase(private val e: TydiEl, val n: Int, val /** * Meta-connect function. Connects all metadata signals but not the data or user signals. * @param bundle Source stream to drive this stream with. + * @param compatCheckResult Whether to report stream compatibility issues as errors or warnings. */ - def :~=( + def :@=( bundle: PhysicalStreamBase )(implicit compatCheckResult: CompatCheckResult.Value = CompatCheckResult.Error): Unit = { paramCheck(bundle, compatCheckResult) @@ -448,18 +449,38 @@ sealed abstract class PhysicalStreamBase(private val e: TydiEl, val n: Int, val } } + /** + * Shortcut for stream mounting with weak type check. + * @param bundle Source stream to drive this stream with. + * @param errorReporting Whether to report stream compatibility issues as errors or warnings. + */ + def :~=( + bundle: PhysicalStreamBase + )(implicit errorReporting: CompatCheckResult.Value = CompatCheckResult.Error): Unit = { + implicit val errorReporting: CompatCheck.Value = CompatCheck.Params + this := bundle + } + + /** + * Stream mounting function. + * @param bundle Source stream to drive this stream with. + * @param errorReporting Whether to report stream compatibility issues as errors or warnings. + * @param typeCheck Whether to conduct a strong or weak type check. A weak type check only verifies the number of bits. + */ def :=(bundle: PhysicalStreamBase)(implicit typeCheck: CompatCheck.Value = CompatCheck.Strict, errorReporting: CompatCheckResult.Value = CompatCheckResult.Error ): Unit = { - this :~= bundle - elementCheckTyped(bundle, typeCheck, errorReporting) + this :@= bundle // Connect meta signals and check parameters + elementCheckTyped(bundle, typeCheck, errorReporting) // Check data types + // Call the right connect method (this, bundle) match { case (x: PhysicalStream, y: PhysicalStream) => x.connectSimple(y, typeCheck, errorReporting) case (x: PhysicalStream, y: PhysicalStreamDetailed[_, _]) => x.connectDetailed(y, typeCheck, errorReporting) case (x: PhysicalStreamDetailed[_, _], y: PhysicalStream) => x.connectSimple(y, typeCheck, errorReporting) case (x: PhysicalStreamDetailed[_, _], y: PhysicalStreamDetailed[_, _]) => x.connectDetailed(y, typeCheck, errorReporting) + case _ => throw new Exception("Could not determine data connection method.") } } diff --git a/testing/src/test/scala/nl/tudelft/tydi_chisel/StreamCompatCheckTest.scala b/testing/src/test/scala/nl/tudelft/tydi_chisel/StreamCompatCheckTest.scala index 28d2dc2..d1676e1 100644 --- a/testing/src/test/scala/nl/tudelft/tydi_chisel/StreamCompatCheckTest.scala +++ b/testing/src/test/scala/nl/tudelft/tydi_chisel/StreamCompatCheckTest.scala @@ -22,7 +22,7 @@ class StreamCompatCheckTest extends AnyFlatSpec with ChiselScalatestTester { val outStream: PhysicalStream = IO(out) { - implicit val typeCheckImplicit: CompatCheck.Value = typeCheckSelect + implicit val typeCheckImplicit: CompatCheck.Value = typeCheckSelect implicit val typeCheckResultImplicit: CompatCheckResult.Value = errorReporting outStream := inStream } @@ -64,7 +64,13 @@ class StreamCompatCheckTest extends AnyFlatSpec with ChiselScalatestTester { it should "weak check type" in { test(new DetailedStreamConnectMod(myBundleStream, myBundle2Stream, CompatCheck.Params)) { _ => } - test(new StreamConnectMod(PhysicalStream(new MyBundle, c = 1), PhysicalStream(new MyBundle2, c = 1), CompatCheck.Params)) { _ => } + test( + new StreamConnectMod( + PhysicalStream(new MyBundle, c = 1), + PhysicalStream(new MyBundle2, c = 1), + CompatCheck.Params + ) + ) { _ => } } it should "check parameters" in { @@ -100,7 +106,7 @@ class StreamCompatCheckTest extends AnyFlatSpec with ChiselScalatestTester { new StreamConnectMod( baseStream, PhysicalStream(new MyBundle, n = 2, d = 1, c = 1, new DataBundle), - errorReporting=CompatCheckResult.Warning + errorReporting = CompatCheckResult.Warning ) ) { _ => } }