@@ -135,6 +135,16 @@ def BidirPinSignature(width, **kwargs):
135135PinList = List [Pin ]
136136Pins = 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
139149class _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
273382class _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
309474PACKAGE_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
317482class 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