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