33import pathlib
44import pydantic
55
6- from dataclasses import dataclass
76from pprint import pformat
87from typing import Set , List , Dict , Optional
98
1615
1716__all__ = ['PIN_ANNOTATION_SCHEMA' , 'PinSignature' ,
1817 'OutputPinSignature' , 'InputPinSignature' , 'BidirPinSignature' ,
19- 'load_pinlock' , "PACKAGE_DEFINITIONS" ]
18+ 'load_pinlock' , "PACKAGE_DEFINITIONS" , 'top_interfaces' ]
2019
2120
2221logger = logging .getLogger (__name__ )
@@ -179,7 +178,7 @@ def allocate(self, available: Set[str], width:int) -> List[str]:
179178 return [str (i ) for i in ret ]
180179
181180PACKAGE_DEFINITIONS = {
182- "pga144" : _PGAPackageDef ("pga144" , 144 )
181+ "pga144" : _PGAPackageDef (name = "pga144" , size = 144 )
183182}
184183
185184
@@ -188,24 +187,80 @@ class Port(pydantic.BaseModel):
188187 pins : List [str ]
189188 direction : Optional [str ] = None
190189
191- Interface = Dict [str , Dict [str , Port ]]
192190
193191class Package (pydantic .BaseModel ):
194192 package_type : PackageDef
195- power : Dict [str , Port ]
196- clocks : Dict [str , Port ]
197- resets : Dict [str , Port ]
193+ power : Dict [str , Port ] = {}
194+ clocks : Dict [str , Port ] = {}
195+ resets : Dict [str , Port ] = {}
196+
197+ def check_pad (self , name : str , defn : dict ):
198+ match defn :
199+ case {"type" : "clock" }:
200+ return self .clocks [name ] if name in self .clocks else None
201+ case {"type" : "reset" }:
202+ return self .resets [name ] if name in self .clocks else None
203+ case {"type" : "source" }:
204+ return self .power [name ] if name in self .clocks else None
205+ case {"type" : "drain" }:
206+ return self .power [name ] if name in self .clocks else None
207+ case _:
208+ return None
209+
210+ def add_pad (self , name :str , defn : dict ):
211+ match defn :
212+ case {"type" : "clock" , "loc" :loc }:
213+ self .clocks [name ] = Port (type = "clock" , pins = [loc ], direction = io .Direction .Input )
214+ case {"type" : "reset" , "loc" :loc }:
215+ self .resets [name ] = Port (type = "reset" , pins = [loc ], direction = io .Direction .Input )
216+ case {"type" : "source" , "loc" :loc }:
217+ self .power [name ] = Port (type = "source" , pins = [loc ])
218+ case {"type" : "drain" , "loc" :loc }:
219+ self .power [name ] = Port (type = "drain" , pins = [loc ])
220+ case _:
221+ pass
222+
223+
224+ class PortMap (pydantic .RootModel [Dict [str , Dict [str , Dict [str , Port ]]]]):
225+ def add_port (self , component :str , interface : str , port_name :str , port : Port ):
226+ if component not in self :
227+ self [component ] = {}
228+ if interface not in self [component ]:
229+ self [component ][interface ] = {}
230+ self [component ][interface ][port_name ] = port
231+
232+ def add_ports (self , component :str , interface : str , ports : Dict [str ,Port ]):
233+ if component not in self :
234+ self [component ] = {}
235+ self [component ][interface ] = ports
236+
237+ def get_ports (self , component :str , name : str ) -> Dict [str , Port ]:
238+ if component not in self :
239+ return None
240+ return self [component ][name ]
241+
198242
199243class LockFile (pydantic .BaseModel ):
244+ """
245+ Representation of a pin lock file.
246+
247+ Attributes:
248+ package: Information about package, power, clocks, reset etc
249+ port_map: Mapping of components to interfaces to port
250+ metadata: Amaranth metadata, for reference
251+ """
200252 package : Package
201- port_map : Interface
253+ port_map : PortMap
202254 metadata : dict
203255
204256
205257def load_pinlock ():
206258 chipflow_root = _ensure_chipflow_root ()
207- json = pathlib .Path (chipflow_root , 'pins.lock' ).read_text ()
208- return LockFile .model_validate_json (json )
259+ lockfile = pathlib .Path (chipflow_root , 'pins.lock' )
260+ if lockfile .exists ():
261+ json = lockfile .read_text ()
262+ return LockFile .model_validate_json (json )
263+ raise ChipFlowError ("Lockfile pins.lock not found. Run `chipflow pin lock`" )
209264
210265
211266def top_interfaces (config ):
0 commit comments