@@ -172,6 +172,16 @@ def BidirPinSignature(width, **kwargs):
172172PinList = List [Pin ]
173173Pins = Union [PinSet , PinList ]
174174
175+ class PowerType (enum .Enum ):
176+ POWER = "power"
177+ GROUND = "ground"
178+
179+ class JTAGWireName (enum .Enum ):
180+ TRST = "trst"
181+ TCK = "tck"
182+ TMS = "tms"
183+ TDI = "tdi"
184+ TDO = "tdo"
175185
176186class _Side (enum .IntEnum ):
177187 N = 1
@@ -264,12 +274,62 @@ class _BasePackageDef(pydantic.BaseModel, abc.ABC):
264274 @property
265275 @abc .abstractmethod
266276 def pins (self ) -> PinSet :
277+ "Returns the full set of pins for the package"
267278 ...
268279
269280 @abc .abstractmethod
270281 def allocate (self , available : PinSet , width : int ) -> PinList :
282+ """
283+ Allocates pins as close to each other as possible from available pins.
284+
285+ Args:
286+ available: set of available pins
287+ width: number of pins to allocate
288+
289+ Returns:
290+ An ordered list of pins
291+ """
292+ ...
293+
294+ @property
295+ @abc .abstractmethod
296+ def power (self ) -> Dict [PowerType , Pin ]:
297+ """
298+ The set of power pins for the package
299+ """
300+ ...
301+
302+ @property
303+ @abc .abstractmethod
304+ def resets (self ) -> Dict [int , Pin ]:
305+ """
306+ Numbered set of reset pins for the package
307+ """
308+ ...
309+
310+ @property
311+ @abc .abstractmethod
312+ def clocks (self ) -> Dict [int , Pin ]:
313+ """
314+ Numbered set of clock pins for the package
315+ """
316+ ...
317+
318+ @property
319+ @abc .abstractmethod
320+ def jtag (self ) -> Dict [JTAGWireName , Pin ]:
321+ """
322+ Map of JTAG pins for the package
323+ """
271324 ...
272325
326+ @property
327+ @abc .abstractmethod
328+ def heartbeat (self ) -> Dict [int , Pin ]:
329+ """
330+ Numbered set of heartbeat pins for the package
331+ """
332+
273333 def to_string (pins : Pins ):
274334 return ['' .join (map (str , t )) for t in pins ]
275335
@@ -306,6 +366,55 @@ def allocate(self, available: PinSet, width: int) -> PinList:
306366 assert len (ret ) == width
307367 return ret
308368
369+ @property
370+ def power (self ) -> Dict [PowerType , Pin ]:
371+ """
372+ The set of power pins for the package
373+ """
374+ # Default implementation - to be customized for specific package types
375+ return {
376+ PowerType .POWER : (_Side .N , 0 ), # North side, pin 0
377+ PowerType .GROUND : (_Side .S , 0 ) # South side, pin 0
378+ }
379+
380+ @property
381+ def resets (self ) -> Dict [int , Pin ]:
382+ """
383+ Numbered set of reset pins for the package
384+ """
385+ # Default implementation with one reset pin
386+ return {0 : (_Side .N , 1 )} # North side, pin 1
387+
388+ @property
389+ def clocks (self ) -> Dict [int , Pin ]:
390+ """
391+ Numbered set of clock pins for the package
392+ """
393+ # Default implementation with one clock pin
394+ return {0 : (_Side .N , 2 )} # North side, pin 2
395+
396+ @property
397+ def jtag (self ) -> Dict [JTAGWireName , Pin ]:
398+ """
399+ Map of JTAG pins for the package
400+ """
401+ # Default JTAG pin allocations
402+ return {
403+ JTAGWireName .TRST : (_Side .W , 0 ), # West side, pin 0
404+ JTAGWireName .TCK : (_Side .W , 1 ), # West side, pin 1
405+ JTAGWireName .TMS : (_Side .W , 2 ), # West side, pin 2
406+ JTAGWireName .TDI : (_Side .W , 3 ), # West side, pin 3
407+ JTAGWireName .TDO : (_Side .W , 4 ) # West side, pin 4
408+ }
409+
410+ @property
411+ def heartbeat (self ) -> Dict [int , Pin ]:
412+ """
413+ Numbered set of heartbeat pins for the package
414+ """
415+ # Default implementation with one heartbeat pin
416+ return {0 : (_Side .S , 1 )} # South side, pin 1
417+
309418
310419class _QuadPackageDef (_BasePackageDef ):
311420 """Definiton of a PGA package with `size` pins
@@ -341,6 +450,62 @@ def allocate(self, available: Set[str], width: int) -> List[str]:
341450 def sortpins (self , pins : Union [List [str ], Set [str ]]) -> List [str ]:
342451 return sorted (list (pins ), key = int )
343452
453+ @property
454+ def power (self ) -> Dict [PowerType , Pin ]:
455+ """
456+ The set of power pins for the package
457+ """
458+ # Default implementation for a PGA package
459+ # Use pin numbers for the corners of the package
460+ total_pins = self .width * 2 + self .height * 2
461+ return {
462+ PowerType .POWER : str (1 ), # First pin
463+ PowerType .GROUND : str (total_pins // 2 ) # Middle pin
464+ }
465+
466+ @property
467+ def resets (self ) -> Dict [int , Pin ]:
468+ """
469+ Numbered set of reset pins for the package
470+ """
471+ # Default implementation with one reset pin
472+ # Use a pin near the beginning of the package
473+ return {0 : str (2 )} # Second pin
474+
475+ @property
476+ def clocks (self ) -> Dict [int , Pin ]:
477+ """
478+ Numbered set of clock pins for the package
479+ """
480+ # Default implementation with one clock pin
481+ # Use a pin near the beginning of the package
482+ return {0 : str (3 )} # Third pin
483+
484+ @property
485+ def jtag (self ) -> Dict [JTAGWireName , Pin ]:
486+ """
487+ Map of JTAG pins for the package
488+ """
489+ # Default JTAG pin allocations
490+ # Use consecutive pins in the middle of the package
491+ mid_pin = (self .width * 2 + self .height * 2 ) // 4
492+ return {
493+ JTAGWireName .TRST : str (mid_pin ),
494+ JTAGWireName .TCK : str (mid_pin + 1 ),
495+ JTAGWireName .TMS : str (mid_pin + 2 ),
496+ JTAGWireName .TDI : str (mid_pin + 3 ),
497+ JTAGWireName .TDO : str (mid_pin + 4 )
498+ }
499+
500+ @property
501+ def heartbeat (self ) -> Dict [int , Pin ]:
502+ """
503+ Numbered set of heartbeat pins for the package
504+ """
505+ # Default implementation with one heartbeat pin
506+ # Use the last pin in the package
507+ return {0 : str (self .width * 2 + self .height * 2 - 1 )}
508+
344509
345510# Add any new package types to both PACKAGE_DEFINITIONS and the PackageDef union
346511PACKAGE_DEFINITIONS = {
@@ -350,7 +515,6 @@ def sortpins(self, pins: Union[List[str], Set[str]]) -> List[str]:
350515
351516PackageDef = Union [_QuadPackageDef , _BareDiePackageDef ]
352517
353-
354518class Port (pydantic .BaseModel ):
355519 type : str
356520 pins : List [str ]
@@ -368,13 +532,15 @@ class Package(pydantic.BaseModel):
368532 power : Dict [str , Port ] = {}
369533 clocks : Dict [str , Port ] = {}
370534 resets : Dict [str , Port ] = {}
535+ jtag : Dict [str , Port ] = {}
536+ heartbeat : Dict [str , Port ] = {}
371537
372538 def check_pad (self , name : str , defn : dict ):
373539 match defn :
374540 case {"type" : "clock" }:
375541 return self .clocks [name ] if name in self .clocks else None
376542 case {"type" : "reset" }:
377- return self .resets [name ] if name in self .clocks else None
543+ return self .resets [name ] if name in self .resets else None
378544 case {"type" : "power" }:
379545 return self .power [name ] if name in self .power else None
380546 case {"type" : "ground" }:
@@ -391,10 +557,46 @@ def add_pad(self, name: str, defn: dict):
391557 case {"type" : "power" , "loc" : loc }:
392558 self .power [name ] = Port (type = "power" , pins = [loc ], port_name = name )
393559 case {"type" : "ground" , "loc" : loc }:
394- self .power [name ] = Port (type = "ground" , pins = [loc ], port_name = name )
560+ self .power [name ] = Port (type = "ground" , pins = [loc ])
561+ case {"type" : "power" , "name" : name , "voltage" : voltage }:
562+ # Support for new power pin format
563+ # First, get the default pin from the package type
564+ power_pin = self .package_type .power [PowerType .POWER ]
565+ self .power [name ] = Port (type = "power" , pins = [str (power_pin )], options = {"voltage" : voltage })
566+ case {"type" : "ground" , "name" : name }:
567+ # Support for new ground pin format
568+ ground_pin = self .package_type .power [PowerType .GROUND ]
569+ self .power [name ] = Port (type = "ground" , pins = [str (ground_pin )])
395570 case _:
396571 pass
397572
573+ def initialize_from_package_type (self ):
574+ """Initialize standard pins from package type definitions"""
575+ # Set up clocks
576+ for clock_id , pin in self .package_type .clocks .items ():
577+ name = f"clock_{ clock_id } "
578+ if name not in self .clocks :
579+ self .clocks [name ] = Port (type = "clock" , pins = [str (pin )], direction = io .Direction .Input )
580+
581+ # Set up resets
582+ for reset_id , pin in self .package_type .resets .items ():
583+ name = f"reset_{ reset_id } "
584+ if name not in self .resets :
585+ self .resets [name ] = Port (type = "reset" , pins = [str (pin )], direction = io .Direction .Input )
586+
587+ # Set up heartbeat pins
588+ for hb_id , pin in self .package_type .heartbeat .items ():
589+ name = f"heartbeat_{ hb_id } "
590+ if name not in self .heartbeat :
591+ self .heartbeat [name ] = Port (type = "heartbeat" , pins = [str (pin )], direction = io .Direction .Output )
592+
593+ # Set up JTAG pins
594+ for jtag_name , pin in self .package_type .jtag .items ():
595+ name = f"jtag_{ jtag_name .value } "
596+ direction = io .Direction .Output if jtag_name == JTAGWireName .TDO else io .Direction .Input
597+ if name not in self .jtag :
598+ self .jtag [name ] = Port (type = "jtag" , pins = [str (pin )], direction = direction )
599+
398600
399601_Interface = Dict [str , Dict [str , Port ]]
400602
0 commit comments