Skip to content

Commit 6e56a7c

Browse files
authored
Revert "Remove deprecated BoringUtils APIs (#4852)" (#4873)
We still have a one usage of this API internally. This reverts commit a143713. Signed-off-by: Schuyler Eldridge <[email protected]>
1 parent afe9b2c commit 6e56a7c

File tree

2 files changed

+205
-2
lines changed

2 files changed

+205
-2
lines changed

src/main/scala/chisel3/util/experimental/BoringUtils.scala

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ import chisel3.probe.{Probe, RWProbe}
77
import chisel3.reflect.DataMirror
88
import chisel3.Data.ProbeInfo
99
import chisel3.experimental.{annotate, requireIsHardware, skipPrefix, BaseModule, SourceInfo}
10-
import chisel3.internal.Builder
10+
import chisel3.internal.{Builder, BuilderContextCache, NamedComponent, Namespace}
1111
import chisel3.internal.binding.{BlockBinding, CrossModuleBinding, PortBinding, SecretPortBinding}
12+
import firrtl.transforms.{DontTouchAnnotation, NoDedupAnnotation}
1213
import chisel3.internal.firrtl.ir.Block
14+
import firrtl.passes.wiring.{SinkAnnotation, SourceAnnotation}
15+
import firrtl.annotations.{ComponentName, ModuleName}
1316

1417
/** An exception related to BoringUtils
1518
* @param message the exception message
@@ -108,6 +111,102 @@ class BoringUtilsException(message: String) extends Exception(message)
108111
* across circuit elaborations.
109112
*/
110113
object BoringUtils {
114+
/* A global namespace for boring ids */
115+
private[chisel3] case object CacheKey extends BuilderContextCache.Key[Namespace]
116+
private def boringNamespace = Builder.contextCache.getOrElseUpdate(CacheKey, Namespace.empty)
117+
118+
/* Get a new name (value) from the namespace */
119+
private def newName(value: String): String = {
120+
boringNamespace.name(value)
121+
}
122+
/* True if the requested name (value) exists in the namespace */
123+
private def checkName(value: String): Boolean = boringNamespace.contains(value)
124+
125+
/** Add a named source cross module reference
126+
* @param component source circuit component
127+
* @param name unique identifier for this source
128+
* @param disableDedup disable deduplication of this source component (this should be true if you are trying to wire
129+
* from specific identical sources differently)
130+
* @param uniqueName if true, this will use a non-conflicting name from the global namespace
131+
* @return the name used
132+
* @note if a uniqueName is not specified, the returned name may differ from the user-provided name
133+
*/
134+
@deprecated(
135+
"Please use the new Boring API instead (BoringUtils.bore(source)). This will be removed in Chisel 7.0",
136+
"Chisel 6.0"
137+
)
138+
def addSource(
139+
component: NamedComponent,
140+
name: String,
141+
disableDedup: Boolean = false,
142+
uniqueName: Boolean = false
143+
): String = {
144+
145+
val id = if (uniqueName) { newName(name) }
146+
else { name }
147+
annotate(component)(
148+
Seq(SourceAnnotation(component.toNamed, id), DontTouchAnnotation(component.toNamed)) ++ Option.when(disableDedup)(
149+
NoDedupAnnotation(component.toNamed.module)
150+
)
151+
)
152+
id
153+
}
154+
155+
/** Add a named sink cross module reference. Multiple sinks may map to the same source.
156+
* @param component sink circuit component
157+
* @param name unique identifier for this sink that must resolve to
158+
* @param disableDedup disable deduplication of this sink component (this should be true if you are trying to wire
159+
* specific, identical sinks differently)
160+
* @param forceExists if true, require that the provided `name` parameter already exists in the global namespace
161+
* @throws BoringUtilsException if name is expected to exist and it doesn't
162+
*/
163+
@deprecated(
164+
"Please use the new Boring API instead (BoringUtils.bore(source)). This will be removed in Chisel 7.0",
165+
"Chisel 6.0"
166+
)
167+
def addSink(
168+
component: InstanceId,
169+
name: String,
170+
disableDedup: Boolean = false,
171+
forceExists: Boolean = false
172+
): Unit = {
173+
174+
if (forceExists && !checkName(name)) {
175+
throw new BoringUtilsException(s"Sink ID '$name' not found in BoringUtils ID namespace")
176+
}
177+
def moduleName = component.toNamed match {
178+
case c: ModuleName => c
179+
case c: ComponentName => c.module
180+
case _ => throw new ChiselException("Can only add a Module or Component sink", null)
181+
}
182+
// annotate doesn't support InstanceId (which is deprecated) because InstanceId doesn't implement toRelativeTarget
183+
// this API is deprecated anyway so probably fine to not check it.
184+
annotate()(Seq(SinkAnnotation(component.toNamed, name)) ++ Option.when(disableDedup)(NoDedupAnnotation(moduleName)))
185+
}
186+
187+
/** Connect a source to one or more sinks
188+
* @param source a source component
189+
* @param sinks one or more sink components
190+
* @return the name of the signal used to connect the source to the
191+
* sinks
192+
* @note the returned name will be based on the name of the source
193+
* component
194+
*/
195+
@deprecated(
196+
"Please use the new Boring API instead (BoringUtils.bore(source)). This will be removed in Chisel 7.0",
197+
"Chisel 6.0"
198+
)
199+
def bore(source: Data, sinks: Seq[Data]): String = {
200+
val boringName =
201+
try {
202+
source.instanceName
203+
} catch {
204+
case _: Exception => "bore"
205+
}
206+
val genName = addSource(source, boringName, true, true)
207+
sinks.foreach(addSink(_, genName, true, true))
208+
genName
209+
}
111210

112211
private def boreOrTap[A <: Data](
113212
source: A,

src/test/scala-2/chiselTests/BoringUtilsSpec.scala

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,116 @@ import firrtl.transforms.DontTouchAnnotation
1717
import org.scalatest.flatspec.AnyFlatSpec
1818
import org.scalatest.matchers.should.Matchers
1919

20+
abstract class ShouldntAssertTester(cyclesToWait: BigInt = 4) extends Module {
21+
val dut: BaseModule
22+
val (_, done) = Counter(true.B, 2)
23+
when(done) { stop() }
24+
}
25+
2026
class BoringUtilsSpec extends AnyFlatSpec with Matchers with LogUtils with FileCheck with ChiselSim {
2127
val args = Array("--throw-on-first-error", "--full-stacktrace")
2228

29+
class BoringInverter extends Module {
30+
val io = IO(new Bundle {})
31+
val a = Wire(UInt(1.W))
32+
val notA = Wire(UInt(1.W))
33+
val b = Wire(UInt(1.W))
34+
a := 0.U
35+
notA := ~a
36+
b := a
37+
chisel3.assert(b === 1.U)
38+
BoringUtils.addSource(notA, "x")
39+
BoringUtils.addSink(b, "x")
40+
}
41+
42+
behavior.of("BoringUtils.addSink and BoringUtils.addSource")
43+
44+
it should "connect two wires within a module" in {
45+
simulate(new ShouldntAssertTester { val dut = Module(new BoringInverter) })(RunUntilFinished(3))
46+
}
47+
48+
trait WireX { this: BaseModule =>
49+
val x = dontTouch(Wire(UInt(4.W)))
50+
}
51+
52+
class Source extends RawModule with WireX {
53+
val in = IO(Input(UInt()))
54+
x := in
55+
}
56+
57+
class Sink extends RawModule with WireX {
58+
val out = IO(Output(UInt()))
59+
x := 0.U // Default value. Output is zero unless we bore...
60+
out := x
61+
}
62+
63+
class Top(val width: Int) extends Module {
64+
/* From the perspective of deduplication, all sources are identical and all sinks are identical. */
65+
val sources = Seq.fill(3)(Module(new Source))
66+
val sinks = Seq.fill(6)(Module(new Sink))
67+
68+
/* Sources are differentiated by their input connections only. */
69+
sources.zip(Seq(0, 1, 2)).map { case (a, b) => a.in := b.U }
70+
71+
/* Sinks are differentiated by their post-boring outputs. */
72+
sinks.zip(Seq(0, 1, 1, 2, 2, 2)).map { case (a, b) => chisel3.assert(a.out === b.U) }
73+
}
74+
75+
/** This is testing a complicated wiring pattern and exercising
76+
* the necessity of disabling deduplication for sources and sinks.
77+
* Without disabling deduplication, this test will fail.
78+
*/
79+
class TopTester extends ShouldntAssertTester {
80+
val dut = Module(new Top(4))
81+
BoringUtils.bore(dut.sources(1).x, Seq(dut.sinks(1).x, dut.sinks(2).x))
82+
BoringUtils.bore(dut.sources(2).x, Seq(dut.sinks(3).x, dut.sinks(4).x, dut.sinks(5).x))
83+
}
84+
85+
class TopTesterFail extends ShouldntAssertTester {
86+
val dut = Module(new Top(4))
87+
BoringUtils.addSource(dut.sources(1).x, "foo", disableDedup = true)
88+
BoringUtils.addSink(dut.sinks(1).x, "foo", disableDedup = true)
89+
BoringUtils.addSink(dut.sinks(2).x, "foo", disableDedup = true)
90+
91+
BoringUtils.addSource(dut.sources(2).x, "bar", disableDedup = true)
92+
BoringUtils.addSink(dut.sinks(3).x, "bar", disableDedup = true)
93+
BoringUtils.addSink(dut.sinks(4).x, "bar", disableDedup = true)
94+
BoringUtils.addSink(dut.sinks(5).x, "bar", disableDedup = true)
95+
}
96+
2397
behavior.of("BoringUtils.bore")
2498

25-
it should "pass a basic test" in {
99+
it should "connect across modules using BoringUtils.bore" in {
100+
simulate(new TopTester)(RunUntilFinished(3))
101+
}
102+
103+
// TODO: this test is not really testing anything as MFC does boring during
104+
// LowerAnnotations (which happens right after parsing). Consider reworking
105+
// this into a test that uses D/I (or some other mechanism of having a
106+
// pre-deduplicated circuit). This is likely better handled as a test in
107+
// CIRCT than in Chisel.
108+
it should "still work even with dedup off" in {
109+
simulate(new TopTesterFail)(RunUntilFinished(3))
110+
}
111+
112+
class InternalBore extends RawModule {
113+
val in = IO(Input(Bool()))
114+
val out = IO(Output(Bool()))
115+
out := false.B
116+
BoringUtils.bore(in, Seq(out))
117+
}
118+
119+
class InternalBoreTester extends ShouldntAssertTester {
120+
val dut = Module(new InternalBore)
121+
dut.in := true.B
122+
chisel3.assert(dut.out === true.B)
123+
}
124+
125+
it should "work for an internal, same module, BoringUtils.bore" in {
126+
simulate(new InternalBoreTester)(RunUntilFinished(3))
127+
}
128+
129+
it should "work using new API" in {
26130
class Baz extends RawModule {
27131
val a_wire = WireInit(UInt(1.W), DontCare)
28132
dontTouch(a_wire)

0 commit comments

Comments
 (0)