Skip to content

Commit 10614d7

Browse files
committed
feat: Interface for determining power, clock, reset, jtag and heartbeat pins
1 parent 02fff02 commit 10614d7

File tree

5 files changed

+477
-27
lines changed

5 files changed

+477
-27
lines changed

chipflow_lib/config_models.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,16 @@ def validate_pad_dict(cls, v: dict, info: ValidationInfo):
3636
return v
3737

3838

39+
Voltage = float
40+
3941
class SiliconConfig(BaseModel):
4042
"""Configuration for silicon in chipflow.toml."""
4143
process: Process
4244
package: Literal["caravel", "cf20", "pga144"]
43-
pads: Dict[str, PadConfig] = {}
44-
power: Dict[str, PadConfig] = {}
45+
power: Dict[str, Voltage] = {}
4546
debug: Optional[Dict[str, bool]] = None
47+
# This is still kept around to allow forcing pad locations.
48+
pads: Optional[Dict[str, PadConfig]] = {}
4649

4750
@field_validator('pads', 'power', mode='before')
4851
@classmethod

chipflow_lib/pin_lock.py

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -103,27 +103,31 @@ def lock_pins() -> None:
103103

104104
package = Package(package_type=package_type)
105105

106-
# Process pads and power configurations using Pydantic models
107-
for d in ("pads", "power"):
108-
logger.debug(f"Checking [chipflow.silicon.{d}]:")
109-
silicon_config = getattr(config_model.chipflow.silicon, d, {})
110-
for k, v in silicon_config.items():
111-
pin = str(v.loc)
112-
used_pins.add(pin)
113-
114-
# Convert Pydantic model to dict for backward compatibility
115-
v_dict = {"type": v.type, "loc": v.loc}
116-
port = oldlock.package.check_pad(k, v_dict) if oldlock else None
117-
118-
if port and port.pins != [pin]:
119-
raise ChipFlowError(
120-
f"chipflow.toml conflicts with pins.lock: "
121-
f"{k} had pin {port.pins}, now {[pin]}."
122-
)
123-
124-
# Add pad to package
125-
package.add_pad(k, v_dict)
126-
106+
# Initialize standard pins from package type
107+
package.initialize_from_package_type()
108+
109+
# Process user-defined pads
110+
logger.debug(f"Checking [chipflow.silicon.pads]:") if hasattr(config_model.chipflow.silicon, "pads") else ...
111+
silicon_config = getattr(config_model.chipflow.silicon, "pads", {})
112+
for k, v in silicon_config.items():
113+
pin = str(v.loc)
114+
used_pins.add(pin)
115+
116+
# Convert Pydantic model to dict for backward compatibility
117+
v_dict = {"type": v.type, "loc": v.loc}
118+
port = oldlock.package.check_pad(k, v_dict) if oldlock else None
119+
120+
if port and port.pins != [pin]:
121+
raise ChipFlowError(
122+
f"chipflow.toml conflicts with pins.lock: "
123+
f"{k} had pin {port.pins}, now {[pin]}."
124+
)
125+
126+
# Add pad to package
127+
package.add_pad(k, v_dict)
128+
129+
# TODO: power pins
130+
#
127131
logger.debug(f'Pins in use: {package_type.sortpins(used_pins)}')
128132

129133
unallocated = package_type.pins - used_pins

chipflow_lib/platforms/utils.py

Lines changed: 207 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,16 @@ def BidirPinSignature(width, **kwargs):
135135
PinList = List[Pin]
136136
Pins = Union[PinSet, PinList]
137137

138+
class PowerType(enum.Enum):
139+
POWER = "power"
140+
GROUND = "ground"
141+
142+
class JTAGWireName(enum.Enum):
143+
TRST = "trst"
144+
TCK = "tck"
145+
TMS = "tms"
146+
TDI = "tdi"
147+
TDO = "tdo"
138148

139149
class _Side(enum.IntEnum):
140150
N = 1
@@ -227,12 +237,62 @@ class _BasePackageDef(pydantic.BaseModel, abc.ABC):
227237
@property
228238
@abc.abstractmethod
229239
def pins(self) -> PinSet:
240+
"Returns the full set of pins for the package"
230241
...
231242

232243
@abc.abstractmethod
233244
def allocate(self, available: PinSet, width: int) -> PinList:
245+
"""
246+
Allocates pins as close to each other as possible from available pins.
247+
248+
Args:
249+
available: set of available pins
250+
width: number of pins to allocate
251+
252+
Returns:
253+
An ordered list of pins
254+
"""
255+
...
256+
257+
@property
258+
@abc.abstractmethod
259+
def power(self) -> Dict[PowerType, Pin]:
260+
"""
261+
The set of power pins for the package
262+
"""
263+
...
264+
265+
@property
266+
@abc.abstractmethod
267+
def resets(self) -> Dict[int, Pin]:
268+
"""
269+
Numbered set of reset pins for the package
270+
"""
234271
...
235272

273+
@property
274+
@abc.abstractmethod
275+
def clocks(self) -> Dict[int, Pin]:
276+
"""
277+
Numbered set of clock pins for the package
278+
"""
279+
...
280+
281+
@property
282+
@abc.abstractmethod
283+
def jtag(self) -> Dict[JTAGWireName, Pin]:
284+
"""
285+
Map of JTAG pins for the package
286+
"""
287+
...
288+
289+
@property
290+
@abc.abstractmethod
291+
def heartbeat(self) -> Dict[int, Pin]:
292+
"""
293+
Numbered set of heartbeat pins for the package
294+
"""
295+
236296
def to_string(pins: Pins):
237297
return [''.join(map(str, t)) for t in pins]
238298

@@ -246,7 +306,7 @@ class _BareDiePackageDef(_BasePackageDef):
246306
"""
247307

248308
# Used by pydantic to differentate when deserialising
249-
type: Literal["_QuadPackageDef"] = "_QuadPackageDef"
309+
type: Literal["_BareDiePackageDef"] = "_BareDiePackageDef"
250310

251311
width: int
252312
height: int
@@ -269,6 +329,55 @@ def allocate(self, available: PinSet, width: int) -> PinList:
269329
assert len(ret) == width
270330
return ret
271331

332+
@property
333+
def power(self) -> Dict[PowerType, Pin]:
334+
"""
335+
The set of power pins for the package
336+
"""
337+
# Default implementation - to be customized for specific package types
338+
return {
339+
PowerType.POWER: (_Side.N, 0), # North side, pin 0
340+
PowerType.GROUND: (_Side.S, 0) # South side, pin 0
341+
}
342+
343+
@property
344+
def resets(self) -> Dict[int, Pin]:
345+
"""
346+
Numbered set of reset pins for the package
347+
"""
348+
# Default implementation with one reset pin
349+
return {0: (_Side.N, 1)} # North side, pin 1
350+
351+
@property
352+
def clocks(self) -> Dict[int, Pin]:
353+
"""
354+
Numbered set of clock pins for the package
355+
"""
356+
# Default implementation with one clock pin
357+
return {0: (_Side.N, 2)} # North side, pin 2
358+
359+
@property
360+
def jtag(self) -> Dict[JTAGWireName, Pin]:
361+
"""
362+
Map of JTAG pins for the package
363+
"""
364+
# Default JTAG pin allocations
365+
return {
366+
JTAGWireName.TRST: (_Side.W, 0), # West side, pin 0
367+
JTAGWireName.TCK: (_Side.W, 1), # West side, pin 1
368+
JTAGWireName.TMS: (_Side.W, 2), # West side, pin 2
369+
JTAGWireName.TDI: (_Side.W, 3), # West side, pin 3
370+
JTAGWireName.TDO: (_Side.W, 4) # West side, pin 4
371+
}
372+
373+
@property
374+
def heartbeat(self) -> Dict[int, Pin]:
375+
"""
376+
Numbered set of heartbeat pins for the package
377+
"""
378+
# Default implementation with one heartbeat pin
379+
return {0: (_Side.S, 1)} # South side, pin 1
380+
272381

273382
class _QuadPackageDef(_BasePackageDef):
274383
"""Definiton of a PGA package with `size` pins
@@ -304,14 +413,70 @@ def allocate(self, available: Set[str], width: int) -> List[str]:
304413
def sortpins(self, pins: Union[List[str], Set[str]]) -> List[str]:
305414
return sorted(list(pins), key=int)
306415

416+
@property
417+
def power(self) -> Dict[PowerType, Pin]:
418+
"""
419+
The set of power pins for the package
420+
"""
421+
# Default implementation for a PGA package
422+
# Use pin numbers for the corners of the package
423+
total_pins = self.width * 2 + self.height * 2
424+
return {
425+
PowerType.POWER: str(1), # First pin
426+
PowerType.GROUND: str(total_pins // 2) # Middle pin
427+
}
428+
429+
@property
430+
def resets(self) -> Dict[int, Pin]:
431+
"""
432+
Numbered set of reset pins for the package
433+
"""
434+
# Default implementation with one reset pin
435+
# Use a pin near the beginning of the package
436+
return {0: str(2)} # Second pin
437+
438+
@property
439+
def clocks(self) -> Dict[int, Pin]:
440+
"""
441+
Numbered set of clock pins for the package
442+
"""
443+
# Default implementation with one clock pin
444+
# Use a pin near the beginning of the package
445+
return {0: str(3)} # Third pin
446+
447+
@property
448+
def jtag(self) -> Dict[JTAGWireName, Pin]:
449+
"""
450+
Map of JTAG pins for the package
451+
"""
452+
# Default JTAG pin allocations
453+
# Use consecutive pins in the middle of the package
454+
mid_pin = (self.width * 2 + self.height * 2) // 4
455+
return {
456+
JTAGWireName.TRST: str(mid_pin),
457+
JTAGWireName.TCK: str(mid_pin + 1),
458+
JTAGWireName.TMS: str(mid_pin + 2),
459+
JTAGWireName.TDI: str(mid_pin + 3),
460+
JTAGWireName.TDO: str(mid_pin + 4)
461+
}
462+
463+
@property
464+
def heartbeat(self) -> Dict[int, Pin]:
465+
"""
466+
Numbered set of heartbeat pins for the package
467+
"""
468+
# Default implementation with one heartbeat pin
469+
# Use the last pin in the package
470+
return {0: str(self.width * 2 + self.height * 2 - 1)}
471+
307472

308473
# Add any new package types to both PACKAGE_DEFINITIONS and the PackageDef union
309474
PACKAGE_DEFINITIONS = {
310475
"pga144": _QuadPackageDef(name="pga144", width=36, height=36),
311476
"cf20": _BareDiePackageDef(name="cf20", width=7, height=3)
312477
}
313478

314-
PackageDef = Union[_QuadPackageDef, _BasePackageDef]
479+
PackageDef = Union[_QuadPackageDef, _BareDiePackageDef, _BasePackageDef]
315480

316481

317482
class Port(pydantic.BaseModel):
@@ -331,13 +496,15 @@ class Package(pydantic.BaseModel):
331496
power: Dict[str, Port] = {}
332497
clocks: Dict[str, Port] = {}
333498
resets: Dict[str, Port] = {}
499+
jtag: Dict[str, Port] = {}
500+
heartbeat: Dict[str, Port] = {}
334501

335502
def check_pad(self, name: str, defn: dict):
336503
match defn:
337504
case {"type": "clock"}:
338505
return self.clocks[name] if name in self.clocks else None
339506
case {"type": "reset"}:
340-
return self.resets[name] if name in self.clocks else None
507+
return self.resets[name] if name in self.resets else None
341508
case {"type": "power"}:
342509
return self.power[name] if name in self.power else None
343510
case {"type": "ground"}:
@@ -354,10 +521,46 @@ def add_pad(self, name: str, defn: dict):
354521
case {"type": "power", "loc": loc}:
355522
self.power[name] = Port(type="power", pins=[loc], port_name=name)
356523
case {"type": "ground", "loc": loc}:
357-
self.power[name] = Port(type="ground", pins=[loc], port_name=name)
524+
self.power[name] = Port(type="ground", pins=[loc])
525+
case {"type": "power", "name": name, "voltage": voltage}:
526+
# Support for new power pin format
527+
# First, get the default pin from the package type
528+
power_pin = self.package_type.power[PowerType.POWER]
529+
self.power[name] = Port(type="power", pins=[str(power_pin)], options={"voltage": voltage})
530+
case {"type": "ground", "name": name}:
531+
# Support for new ground pin format
532+
ground_pin = self.package_type.power[PowerType.GROUND]
533+
self.power[name] = Port(type="ground", pins=[str(ground_pin)])
358534
case _:
359535
pass
360536

537+
def initialize_from_package_type(self):
538+
"""Initialize standard pins from package type definitions"""
539+
# Set up clocks
540+
for clock_id, pin in self.package_type.clocks.items():
541+
name = f"clock_{clock_id}"
542+
if name not in self.clocks:
543+
self.clocks[name] = Port(type="clock", pins=[str(pin)], direction=io.Direction.Input)
544+
545+
# Set up resets
546+
for reset_id, pin in self.package_type.resets.items():
547+
name = f"reset_{reset_id}"
548+
if name not in self.resets:
549+
self.resets[name] = Port(type="reset", pins=[str(pin)], direction=io.Direction.Input)
550+
551+
# Set up heartbeat pins
552+
for hb_id, pin in self.package_type.heartbeat.items():
553+
name = f"heartbeat_{hb_id}"
554+
if name not in self.heartbeat:
555+
self.heartbeat[name] = Port(type="heartbeat", pins=[str(pin)], direction=io.Direction.Output)
556+
557+
# Set up JTAG pins
558+
for jtag_name, pin in self.package_type.jtag.items():
559+
name = f"jtag_{jtag_name.value}"
560+
direction = io.Direction.Output if jtag_name == JTAGWireName.TDO else io.Direction.Input
561+
if name not in self.jtag:
562+
self.jtag[name] = Port(type="jtag", pins=[str(pin)], direction=direction)
563+
361564

362565
_Interface = Dict[str, Dict[str, Port]]
363566

0 commit comments

Comments
 (0)