Skip to content

Commit 6284650

Browse files
committed
Add comments to document GCD example & CanHavePeripheryGCD
Also explains the behavior of the example in IOBinders
1 parent 377450a commit 6284650

File tree

2 files changed

+58
-6
lines changed

2 files changed

+58
-6
lines changed

generators/chipyard/src/main/scala/example/GCD.scala

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,60 +294,90 @@ trait CanHavePeripheryGCD { this: BaseSubsystem =>
294294
val (gcd_busy, gcd_clock) = p(GCDKey) match {
295295
case Some(params) => {
296296

297+
// If externallyClocked is true, create an input port for the GCD clock.
298+
// This clock is distinct from the pbus clock or other internal clocks.
299+
// It's defined within InModuleBody as it's a hardware port.
297300
val gcd_clock = Option.when(params.externallyClocked) {
298301
InModuleBody { IO(Input(Clock())).suggestName("gcd_clock_in") }
299302
}
303+
// Define the clock source node for the GCD module.
300304
val gcdClockNode = if (params.externallyClocked) {
305+
// If externally clocked, create a new ClockSourceNode.
306+
// This node acts as the root of the GCD's independent clock domain.
301307
val gcdSourceClockNode = ClockSourceNode(Seq(ClockSourceParameters()))
302308
InModuleBody {
309+
// Connect the ClockSourceNode's output clock to the external gcd_clock input.
303310
gcdSourceClockNode.out(0)._1.clock := gcd_clock.get
311+
// The reset signal for the GCD's clock domain must be synchronous to the gcd_clock.
312+
// ResetCatchAndSync synchronizes the asynchronous pbus reset to the gcd_clock domain.
304313
gcdSourceClockNode.out(0)._1.reset := ResetCatchAndSync(gcd_clock.get, pbus.module.reset.asBool)
305314
}
306315
gcdSourceClockNode
307316
} else {
317+
// If not externally clocked, the GCD runs on the same clock as the pbus.
308318
pbus.fixedClockNode
309319
}
320+
// Define the type of clock crossing required between the pbus and the GCD module.
310321
val gcdCrossing = if (params.externallyClocked) {
322+
// If the GCD has its own clock, an AsynchronousCrossing is necessary
323+
// to safely transfer data between the pbus clock domain and the GCD clock domain.
311324
AsynchronousCrossing()
312325
} else {
326+
// If the GCD uses the pbus clock, a SynchronousCrossing can be used.
313327
SynchronousCrossing()
314328
}
315329

330+
// Instantiate the GCD module (either TL, AXI4, or HLS variant)
316331
val gcd = if (params.useAXI4) {
317332
val gcd = LazyModule(new GCDAXI4(params, pbus.beatBytes)(p))
333+
// Connect the GCD's clock input to our determined gcdClockNode.
318334
gcd.clockNode := gcdClockNode
335+
// Couple the GCD to the pbus, inserting the necessary clock crossing logic.
319336
pbus.coupleTo(portName) {
337+
// AXI4InwardClockCrossingHelper handles crossing details for AXI4.
320338
AXI4InwardClockCrossingHelper("gcd_crossing", gcd, gcd.node)(gcdCrossing) :=
321339
AXI4Buffer () :=
322340
TLToAXI4 () :=
323-
// toVariableWidthSlave doesn't use holdFirstDeny, which TLToAXI4() needsx
341+
// toVariableWidthSlave doesn't use holdFirstDeny, which TLToAXI4() needs
324342
TLFragmenter(pbus.beatBytes, pbus.blockBytes, holdFirstDeny = true) := _
325343
}
326344
gcd
327345
} else if (params.useHLS) {
328346
val gcd = LazyModule(new HLSGCDAccel(params, pbus.beatBytes)(p))
347+
// Connect the GCD's clock input to our determined gcdClockNode.
329348
gcd.clockNode := gcdClockNode
349+
// Couple the GCD to the pbus, inserting the necessary clock crossing logic.
330350
pbus.coupleTo(portName) {
351+
// TLInwardClockCrossingHelper handles crossing details for TileLink.
331352
TLInwardClockCrossingHelper("gcd_crossing", gcd, gcd.node)(gcdCrossing) :=
332353
TLFragmenter(pbus.beatBytes, pbus.blockBytes) := _
333354
}
334355
gcd
335356
} else {
336357
val gcd = LazyModule(new GCDTL(params, pbus.beatBytes)(p))
358+
// Connect the GCD's clock input to our determined gcdClockNode.
337359
gcd.clockNode := gcdClockNode
360+
// Couple the GCD to the pbus, inserting the necessary clock crossing logic.
338361
pbus.coupleTo(portName) {
362+
// TLInwardClockCrossingHelper handles crossing details for TileLink.
339363
TLInwardClockCrossingHelper("gcd_crossing", gcd, gcd.node)(gcdCrossing) :=
340364
TLFragmenter(pbus.beatBytes, pbus.blockBytes) := _
341365
}
342366
gcd
343367
}
368+
// Expose the GCD's busy signal.
344369
val gcd_busy = InModuleBody {
345370
val busy = IO(Output(Bool())).suggestName("gcd_busy")
346371
busy := gcd.module.io.gcd_busy
347372
busy
348373
}
374+
// Return the busy signal (always needed if GCD exists) and the optional external clock input.
375+
// The Option[Clock] allows the IOBinder (WithGCDBusyPunchthrough) to conditionally
376+
// create the top-level clock input only when `externallyClocked` is true.
377+
// The busy signal is Some(busy) because the entire GCD peripheral itself is optional based on GCDKey.
349378
(Some(gcd_busy), gcd_clock)
350379
}
380+
// If GCDKey is None, the GCD peripheral is not instantiated. Return None for both signals.
351381
case None => (None, None)
352382
}
353383
}

generators/chipyard/src/main/scala/iobinders/IOBinders.scala

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -560,18 +560,40 @@ class WithNMITiedOff extends ComposeIOBinder({
560560
}
561561
})
562562

563+
// This IOBinder connects the signals exposed by the CanHavePeripheryGCD trait
564+
// to the top-level ports of the chip. It handles both the 'busy' signal and
565+
// the optional external clock signal.
563566
class WithGCDBusyPunchthrough extends OverrideIOBinder({
567+
// The input `system` is expected to mix-in CanHavePeripheryGCD.
564568
(system: CanHavePeripheryGCD) => {
569+
// `system.gcd_busy` is an Option[Bool] coming from CanHavePeripheryGCD.
570+
// It is Some[Bool] if the GCD peripheral is present (GCDKey is Some), and None otherwise.
565571
val gcdBusyPort = system.gcd_busy.map { busy =>
566-
val io_gcd_busy = IO(Output(Bool()))
572+
// If `system.gcd_busy` is Some, create a ChipTop port named "gcd_busy".
573+
val io_gcd_busy = IO(Output(Bool())).suggestName("gcd_busy")
574+
// Connect the internal busy signal from the GCD module to this ChipTop port.
567575
io_gcd_busy := busy
576+
// Create a GCDBusyPort entry, (primarily for bookkeeping/reflection in the TestHarness)
568577
GCDBusyPort(() => io_gcd_busy)
569-
}.toSeq
578+
}.toSeq // Convert Option[GCDBusyPort] to Seq[GCDBusyPort] (empty if None)
579+
580+
// `system.gcd_clock` is an Option[Clock] coming from CanHavePeripheryGCD.
581+
// It is Some[Clock] if the GCD peripheral is present AND params.externallyClocked was true.
582+
// It is None if the GCD is not present OR if it's configured to use an internal clock.
570583
val gcdClockPort = system.gcd_clock.map { clock =>
571-
val io_gcd_clock = IO(Input(Clock()))
584+
// If `system.gcd_clock` is Some, create a ChipTop port named "gcd_clock_in".
585+
// This port will be driven by the external clock source in the test harness or board.
586+
val io_gcd_clock = IO(Input(Clock())).suggestName("gcd_clock_in")
587+
// Connect this ChipTop input clock port to the internal clock input wire
588+
// defined within CanHavePeripheryGCD.
589+
// This is what ultimately drives the GCD's ClockSourceNode.
572590
clock := io_gcd_clock
573-
ClockPort(() => io_gcd_clock, freqMHz = 60.0) // freqMHz used in sims
574-
}.toSeq
591+
// Create a ClockPort entry for bookkeeping/reflection.
592+
ClockPort(() => io_gcd_clock, freqMHz = 60.0) // freqMHz used in sims & in the TestHarness
593+
}.toSeq // Convert Option[ClockPort] to Seq[ClockPort] (empty if None)
594+
595+
// Return the sequence of created top-level ports (busy port, and optionally clock port).
596+
// No IOCells are generated here.
575597
(gcdBusyPort ++ gcdClockPort, Nil)
576598
}
577599
})

0 commit comments

Comments
 (0)