Skip to content

Commit 3a9bbfd

Browse files
committed
power domains working?
1 parent 81b3b40 commit 3a9bbfd

File tree

1 file changed

+65
-35
lines changed

1 file changed

+65
-35
lines changed

chipflow_lib/platforms/_utils.py

Lines changed: 65 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class PowerDomain:
7171
class PowerConfig:
7272
domains: Optional[Dict[PowerDomainName, PowerDomain]] = None
7373
map: Optional[Dict[ComponentName,
74-
Dict[InterfaceName, dict]
74+
Dict[InterfaceName, dict | str]
7575
]
7676
] = None
7777

@@ -138,7 +138,7 @@ class Port(pydantic.BaseModel):
138138
pins: List[Pin] | None # None implies must be allocated at end
139139
port_name: str
140140
iomodel: IOModel
141-
ic_power_domain: Optional[PowerDomainName] = None
141+
port_power_domain: Optional[PowerDomainName] = None
142142

143143
@property
144144
def width(self):
@@ -154,15 +154,15 @@ def direction(self):
154154
@property
155155
def invert(self) -> Iterable[bool] | None:
156156
if 'invert' in self.iomodel:
157-
assert self.iomodel['invert'] is tuple
157+
assert isinstance(self.iomodel['invert'], tuple)
158158
return self.iomodel['invert']
159159
else:
160160
return None
161161

162162
@property
163-
def interface_power_domain(self) -> Iterable[bool] | None:
163+
def interface_power_domain(self) -> str | None:
164164
if 'interface_power_domain' in self.iomodel:
165-
assert self.iomodel['interface_power_domain'] is tuple
165+
assert isinstance(self.iomodel['interface_power_domain'], str)
166166
return self.iomodel['interface_power_domain']
167167
else:
168168
return None
@@ -174,6 +174,7 @@ def interface_power_domain(self) -> Iterable[bool] | None:
174174

175175
class PortMap(pydantic.BaseModel):
176176
ports: Dict[str, Component] = {}
177+
port_power_domains: Dict[str, PowerDomain] = {}
177178

178179
def get_ports(self, component: str, interface: str) -> Interface:
179180
"List the ports allocated in this PortMap for the given `Component` and `Interface`"
@@ -217,41 +218,70 @@ def allocate_power(self, config: 'Config'):
217218
"Allocate power domains to top level ports"
218219
if not config.chipflow.power:
219220
return
220-
power_config = config.chipflow.power
221221

222-
def set_iface_pd(c, i, ipd, pd):
223-
for n, p in self.ports[c][i].items():
224-
if p.interface_power_domain == ipd:
225-
p.ic_power_domain = pd
222+
# TODO: allow for shell-defined power domains
223+
if not config.chipflow.power.domains:
224+
return
226225

227-
def match_iface_pd(c, i, x):
228-
for n1, _map in x.items():
229-
for ipd, pd in _map.items():
230-
for n2, p in self.ports[c][i].items():
231-
if n1 == n2 and ipd == p.interface_power_domain:
232-
p.ic_power_domain = pd
226+
# instantiate port-side power domains
227+
self.port_power_domains = config.chipflow.power.domains
233228

234-
if not power_config or not power_config.map:
229+
if not config.chipflow.power.map:
230+
logger.warning("[chipflow.power.domains] is defined, but no [chipflow.power.map] found")
235231
return
236-
for c, im in power_config.map.items():
237-
# c is top-level component name
232+
233+
# convert nested dict structure into a mapping ic_power_domain:[component, interface, {port,} {ip power domain}]
234+
map_config = config.chipflow.power.map
235+
236+
def map_ports(c, i, *, port_power_domain, port_name=None, interface_power_domain=None):
237+
# a bit verbose but easier to understand, I hope..
238+
if port_power_domain not in self.port_power_domains:
239+
raise ChipFlowError(f"'{port_power_domain}' is not a known power domain in {c}.{i}{'.'+port_name if port_name else ''}{'.'+interface_power_domain if interface_power_domain else ''} = '{port_power_domain}')")
240+
if not port_name and interface_power_domain:
241+
for p in self.ports[c][i].values():
242+
if p.interface_power_domain == interface_power_domain:
243+
p.port_power_domain = port_power_domain
244+
elif not port_name:
245+
for p in self.ports[c][i].values():
246+
p.port_power_domain = port_power_domain
247+
elif port_power_domain and not interface_power_domain:
248+
self.ports[c][i][port_name].port_power_domain = port_power_domain
249+
else:
250+
p = self.ports[c][i][port_name]
251+
if p.interface_power_domain == interface_power_domain:
252+
p.port_power_domain = port_power_domain
253+
254+
# must be an easier way to do this...
255+
for c, v in map_config.items():
238256
if c not in self.ports:
239-
raise ChipFlowError(f"In [chipflow.silicon.power], '{c}' is not a top level component")
240-
for i, pm in im.items():
241-
# i is top-level interface name
242-
if i not in self.ports[i]:
243-
raise ChipFlowError(f"In [chipflow.silicon.power], '{i}' is not a top level component of {c}")
244-
for x, y in pm.items():
245-
# is x a power domain of i?
246-
if x is str and any(x == p.interface_power_domain for p in self.ports[c][i].values()):
247-
# set power domain on all items in the port that match
248-
set_iface_pd(c,i,x, y)
249-
# is x a port of i?
250-
if isinstance(x, str) and any(x == p for p in self.ports[c][i].keys()):
251-
self.ports[c][i][x].ic_power_domain = y
252-
# is x a map of portnames to interface power domains?
253-
if isinstance(x, dict) and any(p1 == p1 for p1 in x.keys() for p2 in self.ports[c][i].keys()):
254-
match_iface_pd(c, i, x)
257+
raise ChipFlowError(f"In [chipflow.power.map], '{c}' is not a top level component")
258+
if not isinstance(v, dict):
259+
raise ChipFlowError(f"Malformed [chipflow.power.map] section: {c} = {v}")
260+
for i, v in v.items():
261+
if i not in self.ports[c]:
262+
raise ChipFlowError(f"In [chipflow.silicon.power], '{i}' is not an interface of {c}")
263+
if isinstance(v, str):
264+
map_ports(c, i, port_power_domain=v)
265+
if isinstance(v, dict):
266+
for x, v in v.items():
267+
# is x a port name?
268+
if x in self.ports[c][i]:
269+
if isinstance(v, str):
270+
map_ports(c, i, port_name=x, port_power_domain=v)
271+
elif isinstance(v, dict):
272+
for y, v in v.items():
273+
map_ports(c, i, port_name=x, interface_power_domain=y, port_power_domain=v)
274+
else:
275+
raise ChipFlowError(f"Malformed [chipflow.power.map] section: {c}.{i}.{x} = {v} ('{x}' is a port)")
276+
# is x an interface-side power domain?
277+
elif any(x == p.interface_power_domain for p in self.ports[c][i].values()):
278+
if not isinstance(v, str):
279+
raise ChipFlowError(f"Malformed [chipflow.power.map] section: {c}.{i}.{x} = {v} ('{x}' is a power domaiwn)")
280+
else:
281+
# map interface-side power domain x to port-side power domain v
282+
map_ports(c, i, interface_power_domain=x, port_power_domain=v)
283+
else:
284+
raise ChipFlowError(f"Malformed [chipflow.power.map] section: {c}.{i}.{x} = {v} (unable to interpret '{x}')")
255285

256286

257287
def io_annotation_schema():

0 commit comments

Comments
 (0)