Skip to content

Commit d22734e

Browse files
authored
Merge pull request #2232 from ucb-bar/ext-gcd-clk
Add option for externally clocked GCD peripheral example
2 parents 0e2dd13 + 8a6a19a commit d22734e

File tree

4 files changed

+117
-22
lines changed

4 files changed

+117
-22
lines changed

generators/chipyard/src/main/scala/config/AbstractConfig.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class AbstractConfig extends Config(
6161
new chipyard.iobinders.WithNICIOPunchthrough ++
6262
new chipyard.iobinders.WithTraceIOPunchthrough ++
6363
new chipyard.iobinders.WithUARTTSIPunchthrough ++
64-
new chipyard.iobinders.WithGCDBusyPunchthrough ++
64+
new chipyard.iobinders.WithGCDIOPunchthrough ++
6565
new chipyard.iobinders.WithNMITiedOff ++
6666
new chipyard.iobinders.WithOffchipBusSel ++
6767

generators/chipyard/src/main/scala/config/MMIOAcceleratorConfigs.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ class GCDHLSRocketConfig extends Config(
3232
new freechips.rocketchip.rocket.WithNHugeCores(1) ++
3333
new chipyard.config.AbstractConfig)
3434

35+
class GCDExternallyClockedRocketConfig extends Config(
36+
new chipyard.example.WithGCD(externallyClocked=true)++
37+
new freechips.rocketchip.rocket.WithNHugeCores(1) ++
38+
new chipyard.config.AbstractConfig)
39+
3540
// DOC include start: InitZeroRocketConfig
3641
class InitZeroRocketConfig extends Config(
3742
new chipyard.example.WithInitZero(0x88000000L, 0x1000L) ++ // add InitZero

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

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,19 @@ import org.chipsalliance.cde.config.{Parameters, Field, Config}
1212
import freechips.rocketchip.diplomacy._
1313
import freechips.rocketchip.regmapper.{HasRegMap, RegField}
1414
import freechips.rocketchip.tilelink._
15-
import freechips.rocketchip.util.UIntIsOneOf
15+
import freechips.rocketchip.util._
1616

1717
// DOC include start: GCD params
1818
case class GCDParams(
1919
address: BigInt = 0x4000,
2020
width: Int = 32,
2121
useAXI4: Boolean = false,
2222
useBlackBox: Boolean = true,
23-
useHLS: Boolean = false)
23+
useHLS: Boolean = false,
24+
externallyClocked: Boolean = false
25+
) {
26+
require(!(useAXI4 && useHLS))
27+
}
2428
// DOC include end: GCD params
2529

2630
// DOC include start: GCD key
@@ -287,48 +291,104 @@ trait CanHavePeripheryGCD { this: BaseSubsystem =>
287291
private val pbus = locateTLBusWrapper(PBUS)
288292

289293
// Only build if we are using the TL (nonAXI4) version
290-
val gcd_busy = p(GCDKey) match {
294+
val (gcd_busy, gcd_clock) = p(GCDKey) match {
291295
case Some(params) => {
296+
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.
300+
val gcd_clock = Option.when(params.externallyClocked) {
301+
InModuleBody { IO(Input(Clock())).suggestName("gcd_clock_in") }
302+
}
303+
// Define the clock source node for the GCD module.
304+
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.
307+
val gcdSourceClockNode = ClockSourceNode(Seq(ClockSourceParameters()))
308+
InModuleBody {
309+
// Connect the ClockSourceNode's output clock to the external gcd_clock input.
310+
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.
313+
gcdSourceClockNode.out(0)._1.reset := ResetCatchAndSync(gcd_clock.get, pbus.module.reset.asBool)
314+
}
315+
gcdSourceClockNode
316+
} else {
317+
// If not externally clocked, the GCD runs on the same clock as the pbus.
318+
pbus.fixedClockNode
319+
}
320+
// Define the type of clock crossing required between the pbus and the GCD module.
321+
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.
324+
AsynchronousCrossing()
325+
} else {
326+
// If the GCD uses the pbus clock, a SynchronousCrossing can be used.
327+
SynchronousCrossing()
328+
}
329+
330+
// Instantiate the GCD module (either TL, AXI4, or HLS variant)
292331
val gcd = if (params.useAXI4) {
293332
val gcd = LazyModule(new GCDAXI4(params, pbus.beatBytes)(p))
294-
gcd.clockNode := pbus.fixedClockNode
333+
// Connect the GCD's clock input to our determined gcdClockNode.
334+
gcd.clockNode := gcdClockNode
335+
// Couple the GCD to the pbus, inserting the necessary clock crossing logic.
295336
pbus.coupleTo(portName) {
296-
gcd.node :=
337+
// AXI4InwardClockCrossingHelper handles crossing details for AXI4.
338+
AXI4InwardClockCrossingHelper("gcd_crossing", gcd, gcd.node)(gcdCrossing) :=
297339
AXI4Buffer () :=
298340
TLToAXI4 () :=
299-
// toVariableWidthSlave doesn't use holdFirstDeny, which TLToAXI4() needsx
341+
// toVariableWidthSlave doesn't use holdFirstDeny, which TLToAXI4() needs
300342
TLFragmenter(pbus.beatBytes, pbus.blockBytes, holdFirstDeny = true) := _
301343
}
302344
gcd
303345
} else if (params.useHLS) {
304346
val gcd = LazyModule(new HLSGCDAccel(params, pbus.beatBytes)(p))
305-
gcd.clockNode := pbus.fixedClockNode
306-
pbus.coupleTo(portName) { gcd.node := TLFragmenter(pbus.beatBytes, pbus.blockBytes) := _ }
347+
// Connect the GCD's clock input to our determined gcdClockNode.
348+
gcd.clockNode := gcdClockNode
349+
// Couple the GCD to the pbus, inserting the necessary clock crossing logic.
350+
pbus.coupleTo(portName) {
351+
// TLInwardClockCrossingHelper handles crossing details for TileLink.
352+
TLInwardClockCrossingHelper("gcd_crossing", gcd, gcd.node)(gcdCrossing) :=
353+
TLFragmenter(pbus.beatBytes, pbus.blockBytes) := _
354+
}
307355
gcd
308356
} else {
309357
val gcd = LazyModule(new GCDTL(params, pbus.beatBytes)(p))
310-
gcd.clockNode := pbus.fixedClockNode
311-
pbus.coupleTo(portName) { gcd.node := TLFragmenter(pbus.beatBytes, pbus.blockBytes) := _ }
358+
// Connect the GCD's clock input to our determined gcdClockNode.
359+
gcd.clockNode := gcdClockNode
360+
// Couple the GCD to the pbus, inserting the necessary clock crossing logic.
361+
pbus.coupleTo(portName) {
362+
// TLInwardClockCrossingHelper handles crossing details for TileLink.
363+
TLInwardClockCrossingHelper("gcd_crossing", gcd, gcd.node)(gcdCrossing) :=
364+
TLFragmenter(pbus.beatBytes, pbus.blockBytes) := _
365+
}
312366
gcd
313367
}
368+
// Expose the GCD's busy signal.
314369
val gcd_busy = InModuleBody {
315370
val busy = IO(Output(Bool())).suggestName("gcd_busy")
316371
busy := gcd.module.io.gcd_busy
317372
busy
318373
}
319-
Some(gcd_busy)
374+
// Return the busy signal (always needed if GCD exists) and the optional external clock input.
375+
// The Option[Clock] allows the IOBinder (WithGCDIOPunchthrough) 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.
378+
(Some(gcd_busy), gcd_clock)
320379
}
321-
case None => None
380+
// If GCDKey is None, the GCD peripheral is not instantiated. Return None for both signals.
381+
case None => (None, None)
322382
}
323383
}
324384
// DOC include end: GCD lazy trait
325385

326386
// DOC include start: GCD config fragment
327-
class WithGCD(useAXI4: Boolean = false, useBlackBox: Boolean = false, useHLS: Boolean = false) extends Config((site, here, up) => {
387+
class WithGCD(useAXI4: Boolean = false, useBlackBox: Boolean = false, useHLS: Boolean = false, externallyClocked: Boolean = false) extends Config((site, here, up) => {
328388
case GCDKey => {
329389
// useHLS cannot be used with useAXI4 and useBlackBox
330-
assert(!useHLS || (useHLS && !useAXI4 && !useBlackBox))
331-
Some(GCDParams(useAXI4 = useAXI4, useBlackBox = useBlackBox, useHLS = useHLS))
390+
assert(!useHLS || (useHLS && !useAXI4 && !useBlackBox))
391+
Some(GCDParams(useAXI4 = useAXI4, useBlackBox = useBlackBox, useHLS = useHLS, externallyClocked = externallyClocked))
332392
}
333393
})
334394
// DOC include end: GCD config fragment

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

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -560,12 +560,42 @@ class WithNMITiedOff extends ComposeIOBinder({
560560
}
561561
})
562562

563-
class WithGCDBusyPunchthrough extends OverrideIOBinder({
564-
(system: CanHavePeripheryGCD) => system.gcd_busy.map { busy =>
565-
val io_gcd_busy = IO(Output(Bool()))
566-
io_gcd_busy := busy
567-
(Seq(GCDBusyPort(() => io_gcd_busy)), Nil)
568-
}.getOrElse((Nil, Nil))
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.
566+
class WithGCDIOPunchthrough extends OverrideIOBinder({
567+
// The input `system` is expected to mix-in CanHavePeripheryGCD.
568+
(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.
571+
val gcdBusyPort = system.gcd_busy.map { busy =>
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.
575+
io_gcd_busy := busy
576+
// Create a GCDBusyPort entry, (primarily for bookkeeping/reflection in the TestHarness)
577+
GCDBusyPort(() => io_gcd_busy)
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.
583+
val gcdClockPort = system.gcd_clock.map { 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.
590+
clock := io_gcd_clock
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.
597+
(gcdBusyPort ++ gcdClockPort, Nil)
598+
}
569599
})
570600

571601
class WithOffchipBusSel extends OverrideIOBinder({

0 commit comments

Comments
 (0)