[util] clkmgr groups enhancements
- This commit is necessary to address #8405 and #8037
Modules are now allowed to delcare a different clock group per clock.
This allows for some flexibility in how things are hooked-up.
Further the idea of "internal" clocks are introduced for clocks that
are generated entirely inside a module but still used with the regfile.
This is especially useful for creating CDC structures for blocks like
clkmgr that has locally generated clocks.
Signed-off-by: Timothy Chen <timothytim@google.com>
diff --git a/hw/ip/clkmgr/data/clkmgr.hjson.tpl b/hw/ip/clkmgr/data/clkmgr.hjson.tpl
index 949ff1c..f75e6d0 100644
--- a/hw/ip/clkmgr/data/clkmgr.hjson.tpl
+++ b/hw/ip/clkmgr/data/clkmgr.hjson.tpl
@@ -9,8 +9,11 @@
scan: "true",
clocking: [
{clock: "clk_i", reset: "rst_ni", primary: true},
-% for rst in clocks.reset_signals():
- {reset: "${rst}"},
+% for src in clocks.srcs.values():
+ {clock: "clk_${src.name}_i", reset: "rst_${src.name}_ni"},
+% endfor
+% for src in clocks.derived_srcs.values():
+ {clock: "clk_${src.name}_i", reset: "rst_${src.name}_ni", internal: true},
% endfor
]
bus_interfaces: [
@@ -88,16 +91,6 @@
package: ""
},
- // All clock inputs
-% for src in clocks.srcs.values():
- { struct: "logic",
- type: "uni",
- name: "clk_${src.name}",
- act: "rcv",
- package: "",
- },
-% endfor
-
// Exported clocks
% for intf in cfg['exported_clks']:
{ struct: "clkmgr_${intf}_out",
diff --git a/hw/top_earlgrey/data/top_earlgrey.hjson b/hw/top_earlgrey/data/top_earlgrey.hjson
index cbfe9bb..a481df1 100644
--- a/hw/top_earlgrey/data/top_earlgrey.hjson
+++ b/hw/top_earlgrey/data/top_earlgrey.hjson
@@ -87,6 +87,7 @@
groups: [
// the powerup group is used exclusively by clk/pwr/rstmgr/pinmux
+ { name: "ast", src:"ext", sw_cg: "no" }
{ name: "powerup", src:"top", sw_cg: "no" }
{ name: "trans", src:"top", sw_cg: "hint", unique: "yes", }
{ name: "infra", src:"top", sw_cg: "no", }
@@ -361,7 +362,25 @@
},
{ name: "clkmgr_aon",
type: "clkmgr",
- clock_srcs: {clk_i: "io_div4"},
+ clock_srcs: {
+ clk_i: "io_div4",
+ clk_main_i: {
+ group: "ast",
+ clock: "main"
+ },
+ clk_io_i: {
+ group: "ast",
+ clock: "io"
+ },
+ clk_usb_i: {
+ group: "ast",
+ clock: "usb"
+ },
+ clk_aon_i: {
+ group: "ast",
+ clock: "aon"
+ }
+ },
clock_group: "powerup",
reset_connections: {rst_ni: "por_io_div4",
rst_main_ni: "por",
@@ -897,10 +916,6 @@
'ast.ram_1p_cfg' : 'ram_1p_cfg',
'ast.ram_2p_cfg' : 'ram_2p_cfg',
'ast.rom_cfg' : 'rom_cfg',
- 'clkmgr_aon.clk_main' : 'clk_main', // clock inputs
- 'clkmgr_aon.clk_io' : 'clk_io', // clock inputs
- 'clkmgr_aon.clk_usb' : 'clk_usb', // clock inputs
- 'clkmgr_aon.clk_aon' : 'clk_aon', // clock inputs
'clkmgr_aon.jitter_en' : 'clk_main_jitter_en',
'clkmgr_aon.ast_clk_byp_req' : 'ast_clk_byp_req',
'clkmgr_aon.ast_clk_byp_ack' : 'ast_clk_byp_ack',
diff --git a/hw/top_earlgrey/dv/tb/tb.sv b/hw/top_earlgrey/dv/tb/tb.sv
index 1f3b210..2275662 100644
--- a/hw/top_earlgrey/dv/tb/tb.sv
+++ b/hw/top_earlgrey/dv/tb/tb.sv
@@ -47,7 +47,7 @@
// internal clocks and resets
// cpu clock cannot reference cpu_hier since cpu clocks are forced off in stub_cpu mode
- wire cpu_clk = `CLKMGR_HIER.clocks_o.clk_proc_main;
+ wire cpu_clk = `CPU_HIER.clk_i;
wire cpu_rst_n = `CPU_HIER.rst_ni;
wire alert_handler_clk = `ALERT_HANDLER_HIER.clk_i;
diff --git a/hw/top_englishbreakfast/data/top_englishbreakfast.hjson b/hw/top_englishbreakfast/data/top_englishbreakfast.hjson
index 4720f0d..bd1f1be 100644
--- a/hw/top_englishbreakfast/data/top_englishbreakfast.hjson
+++ b/hw/top_englishbreakfast/data/top_englishbreakfast.hjson
@@ -85,6 +85,8 @@
// The proc group is not peripheral, and directly hardwired
groups: [
+ // the ast group is used to represent input clocks to the top
+ { name: "ast", src:"ext", sw_cg: "no" }
// the powerup group is used exclusively by clk/pwr/rstmgr/pinmux
{ name: "powerup", src:"top", sw_cg: "no" }
{ name: "trans", src:"top", sw_cg: "hint", unique: "yes", }
@@ -245,7 +247,25 @@
},
{ name: "clkmgr_aon",
type: "clkmgr",
- clock_srcs: {clk_i: "io_div4"},
+ clock_srcs: {
+ clk_i: "io_div4",
+ clk_main_i: {
+ group: "ast",
+ clock: "main"
+ },
+ clk_io_i: {
+ group: "ast",
+ clock: "io"
+ },
+ clk_usb_i: {
+ group: "ast",
+ clock: "usb"
+ },
+ clk_aon_i: {
+ group: "ast",
+ clock: "aon"
+ }
+ },
clock_group: "powerup",
reset_connections: {rst_ni: "por_io_div4",
rst_main_ni: "por",
@@ -592,10 +612,6 @@
'ast.ram_1p_cfg' : 'ram_1p_cfg',
'ast.ram_2p_cfg' : 'ram_2p_cfg',
'ast.rom_cfg' : 'rom_cfg',
- 'clkmgr_aon.clk_main' : 'clk_main', // clock inputs
- 'clkmgr_aon.clk_io' : 'clk_io', // clock inputs
- 'clkmgr_aon.clk_usb' : 'clk_usb', // clock inputs
- 'clkmgr_aon.clk_aon' : 'clk_aon', // clock inputs
'clkmgr_aon.jitter_en' : 'clk_main_jitter_en',
'clkmgr_aon.ast_clk_byp_req' : 'ast_clk_byp_req',
'clkmgr_aon.ast_clk_byp_ack' : 'ast_clk_byp_ack',
diff --git a/util/reggen/clocking.py b/util/reggen/clocking.py
index b2330c4..3bec741 100644
--- a/util/reggen/clocking.py
+++ b/util/reggen/clocking.py
@@ -16,6 +16,7 @@
reset: Optional[str],
idle: Optional[str],
primary: bool,
+ internal: bool,
clock_base_name: Optional[str]):
if primary:
assert clock is not None
@@ -26,17 +27,23 @@
self.reset = reset
self.primary = primary
self.idle = idle
+ # Internal means this clock is generated completely internal to the module
+ # and not supplied by the top level.
+ # However, the IpBlock may need to be aware of this clock for CDC purposes
+ self.internal = internal
@staticmethod
def from_raw(raw: object, only_item: bool, where: str) -> 'ClockingItem':
what = f'clocking item at {where}'
- rd = check_keys(raw, what, [], ['clock', 'reset', 'idle', 'primary'])
+ rd = check_keys(raw, what, [], ['clock', 'reset', 'idle', 'primary', 'internal'])
clock = check_optional_name(rd.get('clock'), 'clock field of ' + what)
reset = check_optional_name(rd.get('reset'), 'reset field of ' + what)
idle = check_optional_name(rd.get('idle'), 'idle field of ' + what)
primary = check_bool(rd.get('primary', only_item),
'primary field of ' + what)
+ internal = check_bool(rd.get('internal', False),
+ 'internal field of ' + what)
match = re.match(r'^clk_([A-Za-z0-9_]+)_i', str(clock))
if not clock or clock in ['clk_i', 'scan_clk_i']:
@@ -55,7 +62,7 @@
raise ValueError('No reset signal for primary '
f'clocking item at {what}.')
- return ClockingItem(clock, reset, idle, primary, clock_base_name)
+ return ClockingItem(clock, reset, idle, primary, internal, clock_base_name)
def _asdict(self) -> Dict[str, object]:
ret = {} # type: Dict[str, object]
@@ -107,8 +114,12 @@
ret.append(item.clock)
return ret
- def clock_signals(self) -> List[str]:
- return [item.clock for item in self.items if item.clock is not None]
+ def clock_signals(self, ret_internal: bool = True) -> List[str]:
+ # By default clock_signals returns all clocks, including internal clocks.
+ # If the ret_internal input is set to false, then only externally supplied
+ # clocks are returned.
+ return [item.clock for item in self.items if item.clock is not None and
+ (ret_internal or not item.internal)]
def reset_signals(self) -> List[str]:
return [item.reset for item in self.items if item.reset is not None]
@@ -117,8 +128,8 @@
ret = None
for item in self.items:
if name == item.clock:
- ret = item
- break
+ ret = item
+ break
if ret is None:
raise ValueError(f'The requested clock {name} does not exist.')
diff --git a/util/topgen/clocks.py b/util/topgen/clocks.py
index ea03010..02406ec 100644
--- a/util/topgen/clocks.py
+++ b/util/topgen/clocks.py
@@ -125,6 +125,10 @@
class TypedClocks(NamedTuple):
+ # External clocks that are consumed only inside the clkmgr and are fed from
+ # an external ast source.
+ ast_clks: Dict[str, ClockSignal]
+
# Clocks fed through clkmgr but not disturbed in any way. This maintains
# the clocking structure consistency. This includes two groups of clocks:
#
@@ -260,6 +264,7 @@
def typed_clocks(self) -> TypedClocks:
'''Split the clocks by type'''
+ ast_clks = {}
ft_clks = {}
rg_clks = {}
sw_clks = {}
@@ -273,6 +278,10 @@
continue
for clk, sig in grp.clocks.items():
+ if grp.src == "ext":
+ ast_clks[clk] = sig
+ continue
+
if sig.src.aon:
# Any always-on clock is a feedthrough
ft_clks[clk] = sig
@@ -299,7 +308,8 @@
# Define a canonical ordering for rg_srcs
rg_srcs = list(sorted(rg_srcs_set))
- return TypedClocks(ft_clks=ft_clks,
+ return TypedClocks(ast_clks=ast_clks,
+ ft_clks=ft_clks,
rg_clks=rg_clks,
sw_clks=sw_clks,
hint_clks=hint_clks,
diff --git a/util/topgen/merge.py b/util/topgen/merge.py
index 2f4fe7c..0ecb3be 100644
--- a/util/topgen/merge.py
+++ b/util/topgen/merge.py
@@ -528,6 +528,9 @@
generate the (templated) clkmgr code. This runs before we load up IP blocks
with reggen, so can only see top-level configuration.
+ By default each end point (peripheral, memory etc) is in the same clock group.
+ However, it is possible to define the group attribute per clock if required.
+
'''
clocks = top['clocks']
assert isinstance(clocks, Clocks)
@@ -540,42 +543,52 @@
# Ensure each module has a default case
export_if = ep.get('clock_reset_export', [])
- # if no clock group assigned, default is unique
+ # The clock group attribute in an end point sets the defaut
+ # group for every clock in that end point.
+ #
+ # However, the end point can also override specific clocks to
+ # different groups inside clock_srcs. This is generally not
+ # recommended as it is better to stay consistent. However
+ # if needed, the method is available.
ep['clock_group'] = 'secure' if 'clock_group' not in ep else ep[
'clock_group']
ep_grp = ep['clock_group']
# end point names and clocks
ep_name = ep['name']
- ep_clks = []
-
- group = clocks.groups[ep_grp]
for port, clk in ep['clock_srcs'].items():
- ep_clks.append(clk)
+
+ # If the value of a particular connection is a dict,
+ # there are additional attributes to explore
+ if isinstance(clk, str):
+ group_name = ep_grp
+ src_name = clk
+ else:
+ assert isinstance(clk, Dict)
+ group_name = clk.get('group', ep_grp)
+ src_name = clk['clock']
+
+ group = clocks.groups[group_name]
name = ''
hier_name = clocks.hier_paths[group.src]
if group.src == 'ext':
- # clock comes from top ports
- if clk == 'main':
- name = "i"
- else:
- name = "{}_i".format(clk)
+ name = "{}_i".format(src_name)
elif group.unique:
# new unqiue clock name
- name = "{}_{}".format(clk, ep_name)
+ name = "{}_{}".format(src_name, ep_name)
else:
# new group clock name
- name = "{}_{}".format(clk, ep_grp)
+ name = "{}_{}".format(src_name, group_name)
clk_name = "clk_" + name
# add clock to a particular group
- clk_sig = clocks.add_clock_to_group(group, clk_name, clk)
+ clk_sig = clocks.add_clock_to_group(group, clk_name, src_name)
clk_sig.add_endpoint(ep_name, port)
# add clock connections
diff --git a/util/topgen/templates/toplevel.sv.tpl b/util/topgen/templates/toplevel.sv.tpl
index 820ae5d..b974d25 100644
--- a/util/topgen/templates/toplevel.sv.tpl
+++ b/util/topgen/templates/toplevel.sv.tpl
@@ -93,6 +93,11 @@
% endif
+ // All externally supplied clocks
+ % for clk in top['clocks'].typed_clocks().ast_clks:
+ input ${clk},
+ % endfor
+
// All clocks forwarded to ast
output clkmgr_pkg::clkmgr_out_t clks_ast_o,
output rstmgr_pkg::rstmgr_out_t rsts_ast_o,
diff --git a/util/topgen/validate.py b/util/topgen/validate.py
index cce14a3..68409ec 100644
--- a/util/topgen/validate.py
+++ b/util/topgen/validate.py
@@ -735,7 +735,7 @@
# (generated by topgen for a crossbar)
if isinstance(inst, IpBlock):
name = inst.name
- clock_signals = inst.clocking.clock_signals()
+ clock_signals = inst.clocking.clock_signals(False)
else:
name = inst['name']
clock_signals = ([inst.get('clock_primary', 'rst_ni')] +
@@ -757,9 +757,12 @@
(prefix, name))
[log.error("%s" % port) for port in missing_port]
- missing_net = [
- net for port, net in top['clock_srcs'].items() if net not in clock_srcs
- ]
+ missing_net = []
+ for port, net in top['clock_srcs'].items():
+ net_name = net['clock'] if isinstance(net, Dict) else net
+
+ if net_name not in clock_srcs:
+ missing_net.append(net)
if missing_net:
error += 1