-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
838 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
# Clock Gating | ||
|
||
ROHD-HCL includes a generic clock gating component for enabling and disabling clocks to save power. The implementation supports multiple scenarios and use cases: | ||
|
||
- Easily control whether clock gating `isPresent` or not without modifying the implementation. | ||
- Delay (or don't) controlled signals that are sampled in the gated clock domain, depending on your timing needs. | ||
- Optionally use an override to force all clock gates to be enabled. | ||
- Bring your own clock gating implementation and propagate the instantiation and any additionally required ports through an entire hierarchy without modifying any lower levels of the design. | ||
- Automatically handle some tricky situations (e.g. keeping clocks enabled during reset for synchronous reset). | ||
|
||
![Diagram of the clock gating component](clock_gate.png) | ||
|
||
A very simple counter design is shown below with clock gating included via the component. | ||
|
||
```dart | ||
class CounterWithSimpleClockGate extends Module { | ||
Logic get count => output('count'); | ||
CounterWithSimpleClockGate({ | ||
required Logic clk, | ||
required Logic incr, | ||
required Logic reset, | ||
required ClockGateControlInterface cgIntf, | ||
}) : super(name: 'clk_gated_counter') { | ||
clk = addInput('clk', clk); | ||
incr = addInput('incr', incr); | ||
reset = addInput('reset', reset); | ||
// We clone the incoming interface, receiving all config information with it | ||
cgIntf = ClockGateControlInterface.clone(cgIntf) | ||
..pairConnectIO(this, cgIntf, PairRole.consumer); | ||
// In this case, we want to enable the clock any time we're incrementing | ||
final clkEnable = incr; | ||
// Build the actual clock gate component. | ||
final clkGate = ClockGate( | ||
clk, | ||
enable: clkEnable, | ||
reset: reset, | ||
controlIntf: cgIntf, | ||
); | ||
final count = addOutput('count', width: 8); | ||
count <= | ||
flop( | ||
// access the gated clock from the component | ||
clkGate.gatedClk, | ||
// by default, `controlled` signals are delayed by 1 cycle | ||
count + clkGate.controlled(incr).zeroExtend(count.width), | ||
reset: reset, | ||
); | ||
} | ||
} | ||
``` | ||
|
||
Some important pieces to note here are: | ||
|
||
- The clock gate component is instantiated like any other component | ||
- We pass it a `ClockGateControlInterface` which brings with it any potential custom control. When we punch ports for this design, we use the `clone` constructor, which carries said configuration information. | ||
- We enable the clock any time `incr` is asserted to increment the counter. | ||
- Use the gated clock on the downstream flop for the counter. | ||
- Use a "controlled" version of `incr`, which by default is delayed by one cycle. | ||
|
||
The `ClockGateControlInterface` comes with an optional `enableOverride` which can force the clocks to always be enabled. It also contains a boolean `isPresent` which can control whether clock gating should be generated at all. Since configuration information is automatically carried down through the hierarchy, this means you *can turn on or off clock gating generation through an entire hierarchy without modifying your design*. | ||
|
||
Suppose now we wanted to add our own custom clock gating module implementation. This implementation may require some additional signals as well. When we pass a control interface we can provide some additional arguments to achieve this. For example: | ||
|
||
```dart | ||
ClockGateControlInterface( | ||
additionalPorts: [ | ||
Port('anotherOverride'), | ||
], | ||
gatedClockGenerator: (intf, clk, enable) => CustomClockGatingModule( | ||
clk: clk, | ||
en: enable, | ||
anotherOverride: intf.port('anotherOverride'), | ||
).gatedClk, | ||
); | ||
``` | ||
|
||
Passing in an interface configured like this would mean that any consumers would automatically get the additional ports and new clock gating implementation. Our counter example could get this new method for clock gating and a new port without changing the design of the counter at all. | ||
|
||
An executable version of this example is available in `example/clock_gating_example.dart`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
// Copyright (C) 2024 Intel Corporation | ||
// SPDX-License-Identifier: BSD-3-Clause | ||
// | ||
// clock_gating_example.dart | ||
// Example of how to use clock gating. | ||
// | ||
// 2024 September 24 | ||
// Author: Max Korbel <[email protected]> | ||
|
||
// ignore_for_file: avoid_print | ||
|
||
import 'dart:async'; | ||
|
||
import 'package:rohd/rohd.dart'; | ||
import 'package:rohd_hcl/rohd_hcl.dart'; | ||
import 'package:rohd_vf/rohd_vf.dart'; | ||
|
||
/// A very simple counter that has clock gating internally. | ||
class CounterWithSimpleClockGate extends Module { | ||
Logic get count => output('count'); | ||
|
||
CounterWithSimpleClockGate({ | ||
required Logic clk, | ||
required Logic incr, | ||
required Logic reset, | ||
required ClockGateControlInterface cgIntf, | ||
}) : super(name: 'clk_gated_counter') { | ||
clk = addInput('clk', clk); | ||
incr = addInput('incr', incr); | ||
reset = addInput('reset', reset); | ||
|
||
// We clone the incoming interface, receiving all config information with it | ||
cgIntf = ClockGateControlInterface.clone(cgIntf) | ||
..pairConnectIO(this, cgIntf, PairRole.consumer); | ||
|
||
// In this case, we want to enable the clock any time we're incrementing | ||
final clkEnable = incr; | ||
|
||
// Build the actual clock gate component. | ||
final clkGate = ClockGate( | ||
clk, | ||
enable: clkEnable, | ||
reset: reset, | ||
controlIntf: cgIntf, | ||
delayControlledSignals: true, | ||
); | ||
|
||
final count = addOutput('count', width: 8); | ||
count <= | ||
flop( | ||
// access the gated clock from the component | ||
clkGate.gatedClk, | ||
|
||
// depending on configuration default, `controlled` signals are | ||
// delayed by 1 cycle (in this case we enable it) | ||
count + clkGate.controlled(incr).zeroExtend(count.width), | ||
|
||
reset: reset, | ||
); | ||
} | ||
} | ||
|
||
/// A reference to an external SystemVerilog clock-gating macro. | ||
class CustomClockGateMacro extends Module with CustomSystemVerilog { | ||
Logic get gatedClk => output('gatedClk'); | ||
|
||
CustomClockGateMacro({ | ||
required Logic clk, | ||
required Logic en, | ||
required Logic override, | ||
required Logic anotherOverride, | ||
}) : super(name: 'custom_clock_gate_macro') { | ||
// make sure ports match the SystemVerilog | ||
clk = addInput('clk', clk); | ||
en = addInput('en', en); | ||
override = addInput('override', override); | ||
anotherOverride = addInput('another_override', anotherOverride); | ||
addOutput('gatedClk'); | ||
|
||
// simulation-only behavior | ||
gatedClk <= clk & flop(~clk, en | override | anotherOverride); | ||
} | ||
|
||
// define how to instantiate this custom SystemVerilog | ||
@override | ||
String instantiationVerilog(String instanceType, String instanceName, | ||
Map<String, String> inputs, Map<String, String> outputs) => | ||
'`CUSTOM_CLOCK_GATE(' | ||
'${outputs['gatedClk']}, ' | ||
'${inputs['clk']}, ' | ||
'${inputs['en']}, ' | ||
'${inputs['override']}, ' | ||
'${inputs['another_override']}' | ||
')'; | ||
} | ||
|
||
Future<void> main({bool noPrint = false}) async { | ||
// Build a custom version of the clock gating control interface which uses our | ||
// custom macro. | ||
final customClockGateControlIntf = ClockGateControlInterface( | ||
hasEnableOverride: true, | ||
additionalPorts: [ | ||
// we add an additional override port, for example, which is passed | ||
// automatically down the hierarchy | ||
Port('anotherOverride'), | ||
], | ||
gatedClockGenerator: (intf, clk, enable) => CustomClockGateMacro( | ||
clk: clk, | ||
en: enable, | ||
override: intf.enableOverride!, | ||
anotherOverride: intf.port('anotherOverride'), | ||
).gatedClk, | ||
); | ||
|
||
// Generate a simple clock. This will run along by itself as | ||
// the Simulator goes. | ||
final clk = SimpleClockGenerator(10).clk; | ||
|
||
// ... and some additional signals | ||
final reset = Logic(); | ||
final incr = Logic(); | ||
|
||
final counter = CounterWithSimpleClockGate( | ||
clk: clk, | ||
reset: reset, | ||
incr: incr, | ||
cgIntf: customClockGateControlIntf, | ||
); | ||
|
||
// build the module and attach a waveform viewer for debug | ||
await counter.build(); | ||
|
||
// Let's see what this module looks like as SystemVerilog, so we can pass it | ||
// to other tools. | ||
final systemVerilogCode = counter.generateSynth(); | ||
if (!noPrint) { | ||
print(systemVerilogCode); | ||
} | ||
|
||
// Now let's try simulating! | ||
|
||
// Attach a waveform dumper so we can see what happens. | ||
if (!noPrint) { | ||
WaveDumper(counter); | ||
} | ||
|
||
// Start off with a disabled counter and asserting reset at the start. | ||
incr.inject(0); | ||
reset.inject(1); | ||
|
||
// leave overrides turned off | ||
customClockGateControlIntf.enableOverride!.inject(0); | ||
customClockGateControlIntf.port('anotherOverride').inject(0); | ||
|
||
Simulator.setMaxSimTime(1000); | ||
unawaited(Simulator.run()); | ||
|
||
// wait a bit before dropping reset | ||
await clk.waitCycles(3); | ||
reset.inject(0); | ||
|
||
// wait a bit before raising incr | ||
await clk.waitCycles(5); | ||
incr.inject(1); | ||
|
||
// leave it high for a bit, then drop it | ||
await clk.waitCycles(5); | ||
incr.inject(0); | ||
|
||
// wait a little longer, then end the test | ||
await clk.waitCycles(5); | ||
await Simulator.endSimulation(); | ||
|
||
// Now we can review the waves to see how the gated clock does not toggle | ||
// while gated! | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.