serdes-nextpnr-place.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. #!/usr/bin/env python
  2. from collections import namedtuple
  3. import re
  4. BEL = namedtuple('BEL', 'x y z')
  5. def to_int(s):
  6. return int(re.sub(r'[^\d-]+', '', s))
  7. def split_bel(b):
  8. return BEL(*[to_int(x) for x in b.split('/', 3)])
  9. def find_io_site(lc):
  10. # Check in/out ports
  11. for pn in [ 'I0', 'I1', 'I2', 'I3', 'O' ]:
  12. n = lc.ports[pn].net
  13. if (n is None) or n.name.startswith('$PACKER_'):
  14. continue
  15. pl = [ n.driver ] + list(n.users)
  16. for p in pl:
  17. if (p.cell.type == 'SB_IO') and ('BEL' in p.cell.attrs):
  18. return split_bel(p.cell.attrs['BEL'])
  19. return None
  20. # Find all groups and all LCs
  21. serdes_lcs = {}
  22. serdes_site = {}
  23. for n,c in ctx.cells:
  24. if 'SERDES_GRP' in c.attrs:
  25. # Get group ID
  26. grp = int(c.attrs['SERDES_GRP'],2)
  27. # Append to LCs list and IO site list
  28. serdes_lcs.setdefault(grp, []).append(c)
  29. io_site = find_io_site(c)
  30. if io_site is not None:
  31. if (grp in serdes_site) and (serdes_site[grp] != io_site):
  32. raise RuntimeError('IO site conflict for SERDES group %d (%s vs %s)' % (grp, io_site, serdes_site[grp]))
  33. serdes_site[grp] = io_site
  34. # Split into top / bottmon IO banks
  35. serdes_top = {}
  36. serdes_bot = {}
  37. for grp, site in serdes_site.items():
  38. if site.y == 31:
  39. serdes_top[grp] = site
  40. else:
  41. serdes_bot[grp] = site
  42. # Place them
  43. # (super dumb algo ...)
  44. def place(sites):
  45. # Init set
  46. toplace = sorted(sites.items(), key=lambda x:-x[1].x)
  47. placed = {}
  48. # Scan each possible site in order and place ASAP
  49. for x in range(1,26):
  50. # Skip invalid (SPRAM columns)
  51. if x in [6, 19]:
  52. continue
  53. # Place next one ?
  54. if x >= (toplace[-1][1].x - 1):
  55. placed[toplace.pop()[0]] = x
  56. # Done ?
  57. if not toplace:
  58. break
  59. # Cleanup pass
  60. while True:
  61. # Find a group that could be moved to its preferred X
  62. used = set(placed.values())
  63. for grp, pos in placed.items():
  64. px = sites[grp].x
  65. if (pos != px) and (px not in used) and (px not in [6, 19]):
  66. placed[grp] = sites[grp].x
  67. break
  68. else:
  69. break
  70. # Done
  71. return placed
  72. serdes_top_place = place(serdes_top)
  73. serdes_bot_place = place(serdes_bot)
  74. # Merge results
  75. serdes_place = dict()
  76. serdes_place.update(serdes_top_place)
  77. serdes_place.update(serdes_bot_place)
  78. # Add the final BEL attribute to all LCs
  79. for grp in serdes_place.keys():
  80. x = serdes_place[grp]
  81. for lc in serdes_lcs[grp]:
  82. # Grab attributes
  83. yofs = int(lc.attrs['SERDES_YOFS'], 2)
  84. y = (30-yofs) if serdes_site[grp].y == 31 else (1+yofs)
  85. z = int(lc.attrs['SERDES_Z'], 2)
  86. # Set attribute
  87. lc.setAttr('BEL', 'X%d/Y%d/lc%d' % (x, y, z))
  88. # Clear out
  89. lc.unsetAttr('SERDES_GRP')
  90. lc.unsetAttr('SERDES_YOFS')
  91. lc.unsetAttr('SERDES_Z')