|
13 | 13 | from enum import Enum, IntEnum, StrEnum, auto |
14 | 14 | from math import ceil, floor |
15 | 15 | from typing import ( |
16 | | - Any, Annotated, NamedTuple, Self, |
17 | | - TYPE_CHECKING |
| 16 | + Any, Annotated, NamedTuple, Self, TYPE_CHECKING |
18 | 17 | ) |
19 | 18 | from typing_extensions import ( |
20 | 19 | TypedDict, Unpack, NotRequired |
@@ -267,8 +266,7 @@ def BidirIOSignature(width: int, **kwargs: Unpack[IOModelOptions]): |
267 | 266 | model: IOModel = kwargs | {'width': width, 'direction': io.Direction.Bidir} # type: ignore[reportGeneralTypeIssues] |
268 | 267 | return IOSignature(**model) |
269 | 268 |
|
270 | | - |
271 | | -Pin = Union[Tuple[Any,...], str, int] |
| 269 | +Pin = Any |
272 | 270 | PinSet = Set[Pin] |
273 | 271 | PinList = List[Pin] |
274 | 272 | Pins = Union[PinSet, PinList] |
@@ -403,7 +401,6 @@ def _group_consecutive_items(ordering: PinList, lst: PinList) -> OrderedDict[int |
403 | 401 | d.setdefault(len(g), []).append(g) |
404 | 402 | return d |
405 | 403 |
|
406 | | - |
407 | 404 | def _find_contiguous_sequence(ordering: PinList, lst: PinList, total: int) -> PinList: |
408 | 405 | """Find the next sequence of n consecutive numbers in a sorted list |
409 | 406 |
|
@@ -664,8 +661,8 @@ def _allocate_bringup(self, config: 'Config') -> Component: |
664 | 661 | vss = "vss" |
665 | 662 | vdd = "vdd" |
666 | 663 | if pp.name: |
667 | | - vss = f"vss{pp.name}" |
668 | | - vdd = f"vdd{pp.name}" |
| 664 | + vss = f"{pp.name}vss" |
| 665 | + vdd = f"{pp.name}vdd" |
669 | 666 | powerpins[vss].append(pp.power) |
670 | 667 | powerpins[vdd].append(pp.ground) |
671 | 668 |
|
@@ -769,12 +766,14 @@ def model_post_init(self, __context): |
769 | 766 |
|
770 | 767 | @property |
771 | 768 | def bringup_pins(self) -> BringupPins: |
772 | | - core_power = PowerPins( |
773 | | - (_Side.N, 1), |
774 | | - (_Side.N, 2) |
775 | | - ) |
| 769 | + #TODO, this makes no sense for anything that isn't tiny.. |
| 770 | + core_power = [ |
| 771 | + PowerPins((_Side.N, 1), (_Side.N, 2)), |
| 772 | + PowerPins((_Side.W, 1), (_Side.W, 2), name='d') |
| 773 | + ] |
| 774 | + |
776 | 775 | return BringupPins( |
777 | | - core_power=[core_power], |
| 776 | + core_power=core_power, |
778 | 777 | core_clock=(_Side.N, 3), |
779 | 778 | core_reset=(_Side.N, 3), |
780 | 779 | core_heartbeat=(_Side.E, 1), |
@@ -849,27 +848,34 @@ def _power(self) -> List[PowerPins]: |
849 | 848 | Power pins are always a matched pair in the middle of a side, with the number |
850 | 849 | varying with the size of the package. |
851 | 850 | We don't move power pins from these locations to allow for easier bring up test. |
| 851 | + returns two lists, core power pins and io power pins |
852 | 852 | """ |
853 | | - pins = [] |
| 853 | + pins: List[PowerPins] = [] |
| 854 | + # heuristic for sensible number of power pins for a given size |
854 | 855 | n = (self.width + self.height)//12 |
855 | 856 | # Left |
856 | 857 | p = self.height//2 + self.height//2 |
857 | | - pins.append(PowerPins(p, p +1)) |
| 858 | + assert p > 3 |
| 859 | + pins.append(PowerPins(p-2, p-1)) |
| 860 | + pins.append(PowerPins(p, p+1, name='d')) |
858 | 861 | # Bottom |
859 | 862 | start = self.height |
860 | 863 | if n > 2: |
861 | | - p = start + self.width//2 + self.width//2 |
862 | | - pins.append(PowerPins(p, p+1)) |
| 864 | + p = start + self.width//2 |
| 865 | + pins.append(PowerPins(p-2, p-1)) |
| 866 | + pins.append(PowerPins(p, p+1, name='d')) |
863 | 867 | # Right |
864 | 868 | start = start + self.width |
865 | 869 | if n > 1: |
866 | | - p = start + self.height//2 + self.height//2 |
867 | | - pins.append(PowerPins(p, p+1)) |
| 870 | + p = start + self.height//2 |
| 871 | + pins.append(PowerPins(p-2, p-1)) |
| 872 | + pins.append(PowerPins(p, p+1, name='d')) |
868 | 873 | # Top |
869 | 874 | start = start + self.height |
870 | 875 | if n > 3: |
871 | | - p = start + self.width//2 + self.width//2 |
872 | | - pins.append(PowerPins(p, p+1)) |
| 876 | + p = start + self.width//2 |
| 877 | + pins.append(PowerPins(p-2, p-1)) |
| 878 | + pins.append(PowerPins(p, p+1, name='d')) |
873 | 879 | return pins |
874 | 880 |
|
875 | 881 |
|
@@ -961,75 +967,77 @@ class GAPackageDef(BasePackageDef): |
961 | 967 | width:int |
962 | 968 | height: int |
963 | 969 | layout_type: GALayout= GALayout.FULL |
964 | | - channel_width: Optional[int] |
965 | | - island_width: Optional[int] |
966 | | - missing_pins: Optional[Set[GAPin]] |
967 | | - additional_pins: Optional[Set[GAPin]] |
968 | | - |
969 | | - def model_post_init(self, __context): |
970 | | - def int_to_alpha(i: int): |
971 | | - "Covert int to alpha representation, starting at 1" |
972 | | - valid_letters = "ABCDEFGHJKLMPRSTUVWXY" |
973 | | - out = '' |
974 | | - while i > 0: |
975 | | - char = i % len(valid_letters) |
976 | | - i = i // len(valid_letters) |
977 | | - out = valid_letters[char-1] + out |
978 | | - return out |
979 | | - |
| 970 | + channel_width: Optional[int] = None |
| 971 | + island_width: Optional[int] = None |
| 972 | + missing_pins: Optional[Set[GAPin]] = None |
| 973 | + additional_pins: Optional[Set[GAPin]] = None |
| 974 | + |
| 975 | + @staticmethod |
| 976 | + def _int_to_alpha(i: int): |
| 977 | + "Covert int to alpha representation, starting at 1" |
| 978 | + valid_letters = "ABCDEFGHJKLMPRSTUVWXY" |
| 979 | + out = '' |
| 980 | + while i > 0: |
| 981 | + char = i % len(valid_letters) |
| 982 | + i = i // len(valid_letters) |
| 983 | + out = valid_letters[char-1] + out |
| 984 | + return out |
| 985 | + |
| 986 | + def _get_all_pins(self) -> Tuple[Set[GAPin], Set[GAPin] | None]: |
980 | 987 | def pins_for_range(h1: int, h2: int, w1: int, w2: int) -> Set[GAPin]: |
981 | | - pins = [GAPin(int_to_alpha(h),w) for h in range(h1, h2) for w in range(w1, w2)] |
| 988 | + pins = [GAPin(self._int_to_alpha(h),w) for h in range(h1, h2) for w in range(w1, w2)] |
982 | 989 | return set(pins) |
983 | 990 |
|
984 | | - def sort_by_quadrant(pins: Set[GAPin]) -> List[Pin]: |
985 | | - quadrants:List[Set[GAPin]] = [set(), set(), set(), set()] |
986 | | - midline_h = int_to_alpha(self.height // 2) |
987 | | - midline_w = self.width // 2 |
988 | | - for pin in pins: |
989 | | - if pin.h < midline_h and pin.w < midline_w: |
990 | | - quadrants[0].add(pin) |
991 | | - if pin.h >= midline_h and pin.w < midline_w: |
992 | | - quadrants[1].add(pin) |
993 | | - if pin.h < midline_h and pin.w >= midline_w: |
994 | | - quadrants[2].add(pin) |
995 | | - if pin.h >= midline_h and pin.w >= midline_w: |
996 | | - quadrants[3].add(pin) |
997 | | - ret = [] |
998 | | - for q in range(0,3): |
999 | | - ret.append(sorted(quadrants[q])) |
1000 | | - return ret |
1001 | 991 |
|
1002 | | - self._ordered_pins: List[Pin] = [] |
1003 | 992 | match self.layout_type: |
1004 | 993 | case GALayout.FULL: |
1005 | 994 | pins = pins_for_range(1, self.height, 1, self.width) |
1006 | | - pins -= self.bringup_pins.to_set() |
1007 | | - self._ordered_pins = sort_by_quadrant(pins) |
| 995 | + return (pins, None) |
1008 | 996 |
|
1009 | 997 | case GALayout.PERIMETER: |
1010 | 998 | assert self.channel_width is not None |
1011 | 999 | pins = pins_for_range(1, self.height, 1, self.width) - \ |
1012 | 1000 | pins_for_range(1 + self.channel_width, self.height-self.channel_width, 1 + self.channel_width, self.width - self.channel_width) |
1013 | | - pins -= self.bringup_pins.to_set() |
1014 | | - self._ordered_pins = sort_by_quadrant(pins) |
| 1001 | + return (pins, None) |
1015 | 1002 |
|
1016 | 1003 | case GALayout.ISLAND: |
1017 | 1004 | assert self.channel_width is not None |
1018 | 1005 | assert self.island_width is not None |
1019 | 1006 | outer_pins = pins_for_range(1, self.height, 1, self.width) - \ |
1020 | 1007 | pins_for_range(1 + self.channel_width, self.height-self.channel_width, 1 + self.channel_width, self.width - self.channel_width) |
1021 | | - outer_pins -= self.bringup_pins.to_set() |
1022 | 1008 | inner_pins = pins_for_range(ceil(self.height/ 2 - self.island_width /2), floor(self.height/2 + self.island_width /2), |
1023 | 1009 | ceil(self.width / 2 - self.island_width /2), floor(self.width /2 + self.island_width /2)) |
1024 | | - # TODO, allocate island as power |
1025 | | - self._ordered_pins = sort_by_quadrant(outer_pins) + sorted(inner_pins) |
| 1010 | + return (outer_pins, inner_pins) |
1026 | 1011 |
|
1027 | 1012 | case GALayout.CHANNEL: |
1028 | 1013 | assert self.channel_width is not None |
1029 | 1014 | pins = pins_for_range(1, self.channel_width + 1, 1, self.width) | \ |
1030 | 1015 | pins_for_range(self.height - self.channel_width, self.height, 1, self.width) |
1031 | | - pins -= self.bringup_pins.to_set() |
1032 | | - self._ordered_pins = sort_by_quadrant(pins) |
| 1016 | + return (pins, None) |
| 1017 | + |
| 1018 | + def model_post_init(self, __context): |
| 1019 | + def sort_by_quadrant(pins: Set[GAPin]) -> List[GAPin]: |
| 1020 | + quadrants:List[Set[GAPin]] = [set(), set(), set(), set()] |
| 1021 | + midline_h = self._int_to_alpha(self.height // 2) |
| 1022 | + midline_w = self.width // 2 |
| 1023 | + for pin in pins: |
| 1024 | + if pin.h < midline_h and pin.w < midline_w: |
| 1025 | + quadrants[0].add(pin) |
| 1026 | + if pin.h >= midline_h and pin.w < midline_w: |
| 1027 | + quadrants[1].add(pin) |
| 1028 | + if pin.h < midline_h and pin.w >= midline_w: |
| 1029 | + quadrants[2].add(pin) |
| 1030 | + if pin.h >= midline_h and pin.w >= midline_w: |
| 1031 | + quadrants[3].add(pin) |
| 1032 | + ret = [] |
| 1033 | + for q in range(0,3): |
| 1034 | + ret.extend(sorted(quadrants[q])) |
| 1035 | + return ret |
| 1036 | + |
| 1037 | + self._ordered_pins: List[GAPin] = [] |
| 1038 | + pins, _ = self._get_all_pins() |
| 1039 | + pins -= self.bringup_pins.to_set() |
| 1040 | + self._ordered_pins = sort_by_quadrant(pins) |
1033 | 1041 |
|
1034 | 1042 | return super().model_post_init(__context) |
1035 | 1043 |
|
@@ -1061,8 +1069,31 @@ def bringup_pins(self) -> BringupPins: |
1061 | 1069 |
|
1062 | 1070 | @property |
1063 | 1071 | def _power(self) -> List[PowerPins]: |
1064 | | - return [PowerPins(1,2)] |
| 1072 | + #TODO build an internal padring mapping |
| 1073 | + # for now, just distribute evenly |
| 1074 | + power_pins = [] |
1065 | 1075 |
|
| 1076 | + pins, inner = self._get_all_pins() |
| 1077 | + #allocate all of inner to core pins, alternating |
| 1078 | + try: |
| 1079 | + if inner: |
| 1080 | + it = iter(sorted(inner)) |
| 1081 | + for p in it: |
| 1082 | + power_pins.append(PowerPins(p, next(it))) |
| 1083 | + except StopIteration: |
| 1084 | + pass |
| 1085 | + # distribute the rest evenly |
| 1086 | + try: |
| 1087 | + it = iter(sorted(pins)) |
| 1088 | + for p in it: |
| 1089 | + for name in ('','d'): |
| 1090 | + power_pins.append(PowerPins(p, next(it))) |
| 1091 | + for i in range(0,15): |
| 1092 | + next(it) |
| 1093 | + except StopIteration: |
| 1094 | + pass |
| 1095 | + |
| 1096 | + return power_pins |
1066 | 1097 |
|
1067 | 1098 | @property |
1068 | 1099 | def _jtag(self) -> JTAGPins: |
|
0 commit comments