Skip to content

Commit cf2bc92

Browse files
committed
Complete package pin interface implementation
1 parent f3f6894 commit cf2bc92

File tree

4 files changed

+403
-7
lines changed

4 files changed

+403
-7
lines changed

chipflow_lib/pin_lock.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,27 @@ def lock_pins() -> None:
9191
package_type = PACKAGE_DEFINITIONS[package_name]
9292

9393
package = Package(package_type=package_type)
94+
95+
# Initialize standard pins from package type
96+
package.initialize_from_package_type()
97+
98+
# Process user-defined pads and power pins
9499
for d in ("pads", "power"):
100+
if d not in config["chipflow"]["silicon"]:
101+
continue
102+
95103
logger.debug(f"Checking [chipflow.silicon.{d}]:")
96104
_map = {}
97105
for k, v in config["chipflow"]["silicon"][d].items():
98-
pin = str(v['loc'])
99-
used_pins.add(pin)
106+
# Handle both old-style (loc) and new-style (name/voltage) configurations
107+
pin = str(v['loc']) if 'loc' in v else None
108+
109+
# Mark the pin as used if it has a location
110+
if pin:
111+
used_pins.add(pin)
112+
100113
port = oldlock.package.check_pad(k, v) if oldlock else None
101-
if port and port.pins != [pin]:
114+
if port and pin and port.pins != [pin]:
102115
raise ChipFlowError(
103116
f"chipflow.toml conflicts with pins.lock: "
104117
f"{k} had pin {port.pins}, now {[pin]}."

chipflow_lib/platforms/utils.py

Lines changed: 147 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ def jtag(self) -> Dict[JTAGWireName, Pin]:
278278

279279
@property
280280
@abc.abstractmethod
281-
def heartbeat(self) -> Dict(int, Pin):
281+
def heartbeat(self) -> Dict[int, Pin]:
282282
"""
283283
Numbered set of heartbeat pins for the package
284284
"""
@@ -298,7 +298,7 @@ class _BareDiePackageDef(_BasePackageDef):
298298
"""
299299

300300
# Used by pydantic to differentate when deserialising
301-
type: Literal["_QuadPackageDef"] = "_QuadPackageDef"
301+
type: Literal["_BareDiePackageDef"] = "_BareDiePackageDef"
302302

303303
width: int
304304
height: int
@@ -320,6 +320,55 @@ def allocate(self, available: PinSet, width: int) -> PinList:
320320
logger.debug(f"_BareDiePackageDef.returned {ret}")
321321
assert len(ret) == width
322322
return ret
323+
324+
@property
325+
def power(self) -> Dict[PowerType, Pin]:
326+
"""
327+
The set of power pins for the package
328+
"""
329+
# Default implementation - to be customized for specific package types
330+
return {
331+
PowerType.POWER: (_Side.N, 0), # North side, pin 0
332+
PowerType.GROUND: (_Side.S, 0) # South side, pin 0
333+
}
334+
335+
@property
336+
def resets(self) -> Dict[int, Pin]:
337+
"""
338+
Numbered set of reset pins for the package
339+
"""
340+
# Default implementation with one reset pin
341+
return {0: (_Side.N, 1)} # North side, pin 1
342+
343+
@property
344+
def clocks(self) -> Dict[int, Pin]:
345+
"""
346+
Numbered set of clock pins for the package
347+
"""
348+
# Default implementation with one clock pin
349+
return {0: (_Side.N, 2)} # North side, pin 2
350+
351+
@property
352+
def jtag(self) -> Dict[JTAGWireName, Pin]:
353+
"""
354+
Map of JTAG pins for the package
355+
"""
356+
# Default JTAG pin allocations
357+
return {
358+
JTAGWireName.TRST: (_Side.W, 0), # West side, pin 0
359+
JTAGWireName.TCK: (_Side.W, 1), # West side, pin 1
360+
JTAGWireName.TMS: (_Side.W, 2), # West side, pin 2
361+
JTAGWireName.TDI: (_Side.W, 3), # West side, pin 3
362+
JTAGWireName.TDO: (_Side.W, 4) # West side, pin 4
363+
}
364+
365+
@property
366+
def heartbeat(self) -> Dict[int, Pin]:
367+
"""
368+
Numbered set of heartbeat pins for the package
369+
"""
370+
# Default implementation with one heartbeat pin
371+
return {0: (_Side.S, 1)} # South side, pin 1
323372

324373

325374
class _QuadPackageDef(_BasePackageDef):
@@ -355,6 +404,62 @@ def allocate(self, available: Set[str], width: int) -> List[str]:
355404

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

359464

360465
# Add any new package types to both PACKAGE_DEFINITIONS and the PackageDef union
@@ -363,7 +468,7 @@ def sortpins(self, pins: Union[List[str], Set[str]]) -> List[str]:
363468
"cf20": _BareDiePackageDef(name="cf20", width=7, height=3)
364469
}
365470

366-
PackageDef = Union[_QuadPackageDef, _BasePackageDef]
471+
PackageDef = Union[_QuadPackageDef, _BareDiePackageDef, _BasePackageDef]
367472

368473

369474
class Port(pydantic.BaseModel):
@@ -382,13 +487,15 @@ class Package(pydantic.BaseModel):
382487
power: Dict[str, Port] = {}
383488
clocks: Dict[str, Port] = {}
384489
resets: Dict[str, Port] = {}
490+
jtag: Dict[str, Port] = {}
491+
heartbeat: Dict[str, Port] = {}
385492

386493
def check_pad(self, name: str, defn: dict):
387494
match defn:
388495
case {"type": "clock"}:
389496
return self.clocks[name] if name in self.clocks else None
390497
case {"type": "reset"}:
391-
return self.resets[name] if name in self.clocks else None
498+
return self.resets[name] if name in self.resets else None
392499
case {"type": "power"}:
393500
return self.power[name] if name in self.power else None
394501
case {"type": "ground"}:
@@ -406,8 +513,44 @@ def add_pad(self, name: str, defn: dict):
406513
self.power[name] = Port(type="power", pins=[loc])
407514
case {"type": "ground", "loc": loc}:
408515
self.power[name] = Port(type="ground", pins=[loc])
516+
case {"type": "power", "name": name, "voltage": voltage}:
517+
# Support for new power pin format
518+
# First, get the default pin from the package type
519+
power_pin = self.package_type.power[PowerType.POWER]
520+
self.power[name] = Port(type="power", pins=[str(power_pin)], options={"voltage": voltage})
521+
case {"type": "ground", "name": name}:
522+
# Support for new ground pin format
523+
ground_pin = self.package_type.power[PowerType.GROUND]
524+
self.power[name] = Port(type="ground", pins=[str(ground_pin)])
409525
case _:
410526
pass
527+
528+
def initialize_from_package_type(self):
529+
"""Initialize standard pins from package type definitions"""
530+
# Set up clocks
531+
for clock_id, pin in self.package_type.clocks.items():
532+
name = f"clock_{clock_id}"
533+
if name not in self.clocks:
534+
self.clocks[name] = Port(type="clock", pins=[str(pin)], direction=io.Direction.Input)
535+
536+
# Set up resets
537+
for reset_id, pin in self.package_type.resets.items():
538+
name = f"reset_{reset_id}"
539+
if name not in self.resets:
540+
self.resets[name] = Port(type="reset", pins=[str(pin)], direction=io.Direction.Input)
541+
542+
# Set up heartbeat pins
543+
for hb_id, pin in self.package_type.heartbeat.items():
544+
name = f"heartbeat_{hb_id}"
545+
if name not in self.heartbeat:
546+
self.heartbeat[name] = Port(type="heartbeat", pins=[str(pin)], direction=io.Direction.Output)
547+
548+
# Set up JTAG pins
549+
for jtag_name, pin in self.package_type.jtag.items():
550+
name = f"jtag_{jtag_name.value}"
551+
direction = io.Direction.Output if jtag_name == JTAGWireName.TDO else io.Direction.Input
552+
if name not in self.jtag:
553+
self.jtag[name] = Port(type="jtag", pins=[str(pin)], direction=direction)
411554

412555

413556
_Interface = Dict[str, Dict[str, Port]]

docs/package_pins.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Package Pin Interface in ChipFlow
2+
3+
This document describes the package pin interface in ChipFlow, introduced to provide a more structured and consistent way to specify pin configurations for chip packages.
4+
5+
## Overview
6+
7+
The package pin interface provides definitions for various types of pins in a chip package:
8+
9+
- Power and ground pins
10+
- Clock pins
11+
- Reset pins
12+
- JTAG pins
13+
- Heartbeat pins
14+
15+
Each package type (PGA, bare die, etc.) defines its own implementation of these pin types, with appropriate pin numbering and allocation strategies.
16+
17+
## Configuration in TOML Files
18+
19+
### Legacy Format (still supported)
20+
```toml
21+
[chipflow.silicon.power]
22+
vdd = { type = "power", loc = "1" }
23+
gnd = { type = "ground", loc = "2" }
24+
25+
[chipflow.silicon.pads]
26+
reset = { type = "reset", loc = "3" }
27+
clock = { type = "clock", loc = "4" }
28+
```
29+
30+
### New Format
31+
```toml
32+
[chipflow.silicon.power]
33+
vdd = { type = "power", name = "vdd", voltage = "1.8V" }
34+
gnd = { type = "ground", name = "gnd" }
35+
```
36+
37+
In the new format, the package definition provides default locations for standard pins like power, ground, clocks, and resets. You only need to specify the name and properties.
38+
39+
## Using the Package Pin Interface in Code
40+
41+
### Getting Default Pins
42+
43+
```python
44+
from chipflow_lib.platforms.utils import PACKAGE_DEFINITIONS, PowerType, JTAGWireName
45+
46+
# Get a package definition
47+
package_def = PACKAGE_DEFINITIONS["pga144"]
48+
49+
# Get power pins
50+
power_pins = package_def.power
51+
vdd_pin = power_pins[PowerType.POWER] # Get the default power pin
52+
gnd_pin = power_pins[PowerType.GROUND] # Get the default ground pin
53+
54+
# Get clock pins
55+
clock_pins = package_def.clocks
56+
default_clock = clock_pins[0] # Get the first clock pin
57+
58+
# Get JTAG pins
59+
jtag_pins = package_def.jtag
60+
tck_pin = jtag_pins[JTAGWireName.TCK] # Get the TCK pin
61+
tms_pin = jtag_pins[JTAGWireName.TMS] # Get the TMS pin
62+
```
63+
64+
### Creating a Package with Default Pins
65+
66+
```python
67+
from chipflow_lib.platforms.utils import Package, PACKAGE_DEFINITIONS
68+
69+
# Create a package with a specific package definition
70+
package = Package(package_type=PACKAGE_DEFINITIONS["pga144"])
71+
72+
# Initialize default pins from the package definition
73+
package.initialize_from_package_type()
74+
```
75+
76+
## Extending for New Package Types
77+
78+
To create a new package type, you need to:
79+
80+
1. Subclass `_BasePackageDef` and implement all the required properties and methods
81+
2. Add your new package type to the `PackageDef` union and `PACKAGE_DEFINITIONS` dictionary
82+
83+
Example:
84+
85+
```python
86+
class MyNewPackageDef(_BasePackageDef):
87+
type: Literal["MyNewPackageDef"] = "MyNewPackageDef"
88+
# ... implement all required methods ...
89+
90+
# Add to the union
91+
PackageDef = Union[_QuadPackageDef, _BareDiePackageDef, MyNewPackageDef, _BasePackageDef]
92+
93+
# Add to the dictionary of available packages
94+
PACKAGE_DEFINITIONS["my_new_package"] = MyNewPackageDef(name="my_new_package", ...)
95+
```
96+
97+
## Running Tests
98+
99+
Tests for the package pin interface can be run using:
100+
101+
```bash
102+
pdm run pytest tests/test_package_pins.py
103+
```

0 commit comments

Comments
 (0)