| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # |
| # Copyright (C) 2017-2020 The Project X-Ray Authors. |
| # |
| # Use of this source code is governed by a ISC-style |
| # license that can be found in the LICENSE file or at |
| # https://opensource.org/licenses/ISC |
| # |
| # SPDX-License-Identifier: ISC |
| import json |
| import io |
| import os |
| import random |
| random.seed(int(os.getenv("SEED"), 16)) |
| from prjxray import util |
| from prjxray import lut_maker |
| from prjxray import verilog |
| from prjxray.db import Database |
| |
| |
| def gen_sites(): |
| ''' |
| IOB33S: main IOB of a diff pair |
| IOB33M: secondary IOB of a diff pair |
| IOB33: not a diff pair. Relatively rare (at least in ROI...2 of them?) |
| Focus on IOB33S to start |
| ''' |
| db = Database(util.get_db_root(), util.get_part()) |
| grid = db.grid() |
| for tile_name in sorted(grid.tiles()): |
| loc = grid.loc_of_tilename(tile_name) |
| gridinfo = grid.gridinfo_at_loc(loc) |
| |
| sites = {} |
| for site_name, site_type in gridinfo.sites.items(): |
| if site_type in ['IOB33S', 'IOB33M']: |
| sites[site_type] = site_name |
| |
| if sites: |
| yield tile_name, sites |
| |
| |
| def write_params(params): |
| pinstr = 'tile,site,pin,iostandard,drive,slew\n' |
| for vals in params: |
| pinstr += ','.join(map(str, vals)) + '\n' |
| |
| open('params.csv', 'w').write(pinstr) |
| |
| |
| def run(): |
| tile_types = ['IBUF', 'OBUF', 'IOBUF_INTERMDISABLE', None, None] |
| |
| i_idx = 0 |
| o_idx = 0 |
| io_idx = 0 |
| |
| iostandards = [ |
| 'LVCMOS12', |
| 'LVCMOS15', |
| 'LVCMOS18', |
| 'LVCMOS25', |
| 'LVCMOS33', |
| 'LVTTL', |
| 'SSTL135', |
| 'SSTL15', |
| 'LVDS_25', |
| 'TMDS_33', |
| ] |
| |
| diff_map = { |
| "SSTL135": ["DIFF_SSTL135"], |
| "SSTL15": ["DIFF_SSTL15"], |
| } |
| |
| only_diff_map = { |
| "LVDS_25": ["LVDS_25"], |
| "TMDS_33": ["TMDS_33"], |
| } |
| |
| IN_TERM_ALLOWED = [ |
| 'SSTL15', |
| 'SSTL15_R', |
| 'SSTL18', |
| 'SSTL18_R', |
| 'SSTL135', |
| 'SSTL135_R', |
| 'HSTL_I' |
| 'HSTL_I_18' |
| 'HSTL_II', |
| 'HSTL_II_18', |
| ] |
| |
| slews = ['FAST', 'SLOW'] |
| pulls = ["NONE", "KEEPER", "PULLDOWN", "PULLUP"] |
| |
| luts = lut_maker.LutMaker() |
| |
| connects = io.StringIO() |
| |
| tile_params = [] |
| params = { |
| "tiles": [], |
| 'INTERNAL_VREF': {}, |
| } |
| |
| with open(os.path.join(os.getenv('FUZDIR'), 'build', 'iobanks.txt')) as f: |
| iobanks = set() |
| iob_sites = dict() |
| for l in f: |
| fields = l.split(',') |
| iob_site = fields[0] |
| iobank = fields[1].rstrip() |
| |
| iobanks.add(iobank) |
| iob_sites[iob_site] = iobank |
| |
| params['iobanks'] = iobanks |
| |
| iostandard_map = dict() |
| for iobank in iobanks: |
| iostandard = random.choice(iostandards) |
| if iostandard in ['SSTL135', 'SSTL15']: |
| params['INTERNAL_VREF'][iobank] = random.choice( |
| ( |
| .600, |
| .675, |
| .75, |
| .90, |
| )) |
| |
| iostandard_map[iobank] = iostandard |
| |
| params['iobanks'] = list(iobanks) |
| |
| any_idelay = False |
| for tile, sites in gen_sites(): |
| iostandard = None |
| |
| site_bels = {} |
| for site_type, site_name in sites.items(): |
| iobank = iob_sites[site_name] |
| iostandard = iostandard_map[iobank] |
| |
| if iostandard in ['LVTTL', 'LVCMOS18']: |
| drives = [4, 8, 12, 16, 24] |
| elif iostandard in ['LVCMOS12']: |
| drives = [4, 8, 12] |
| elif iostandard in ['SSTL135', 'SSTL15', 'LVDS_25', 'TMDS_33']: |
| drives = None |
| else: |
| drives = [4, 8, 12, 16] |
| |
| if site_type.endswith('M'): |
| if iostandard in diff_map: |
| site_bels[site_type] = random.choice( |
| tile_types + ['IBUFDS', 'OBUFDS', 'OBUFTDS']) |
| elif iostandard in only_diff_map: |
| site_bels[site_type] = random.choice( |
| ['IBUFDS', 'OBUFDS', 'OBUFTDS', None, None]) |
| else: |
| site_bels[site_type] = random.choice(tile_types) |
| is_m_diff = site_bels[site_type] is not None and site_bels[ |
| site_type].endswith('DS') |
| else: |
| site_bels[site_type] = random.choice(tile_types) |
| |
| if is_m_diff or iostandard in only_diff_map: |
| site_bels['IOB33S'] = None |
| |
| for site_type, site in sites.items(): |
| p = {} |
| p['tile'] = tile |
| p['site'] = site |
| p['type'] = site_bels[site_type] |
| |
| if p['type'] is not None and p['type'].endswith('DS'): |
| if iostandard in diff_map: |
| iostandard_site = random.choice(diff_map[iostandard]) |
| elif iostandard in only_diff_map: |
| iostandard_site = random.choice(only_diff_map[iostandard]) |
| p['pair_site'] = sites['IOB33S'] |
| else: |
| iostandard_site = iostandard |
| |
| p['IOSTANDARD'] = verilog.quote(iostandard_site) |
| p['PULLTYPE'] = verilog.quote(random.choice(pulls)) |
| |
| if p['type'] is None: |
| p['pad_wire'] = None |
| elif p['type'] == 'IBUF': |
| p['pad_wire'] = 'di[{}]'.format(i_idx) |
| p['IDELAY_ONLY'] = random.randint(0, 1) |
| if not p['IDELAY_ONLY']: |
| p['owire'] = luts.get_next_input_net() |
| else: |
| any_idelay = True |
| p['owire'] = 'idelay_{site}'.format(**p) |
| |
| p['DRIVE'] = None |
| p['SLEW'] = None |
| p['IBUF_LOW_PWR'] = random.randint(0, 1) |
| |
| if iostandard in IN_TERM_ALLOWED: |
| p['IN_TERM'] = random.choice( |
| ( |
| 'NONE', |
| 'UNTUNED_SPLIT_40', |
| 'UNTUNED_SPLIT_50', |
| 'UNTUNED_SPLIT_60', |
| )) |
| |
| i_idx += 1 |
| elif p['type'] == 'IBUFDS': |
| p['pad_wire'] = 'di[{}]'.format(i_idx) |
| i_idx += 1 |
| p['bpad_wire'] = 'di[{}]'.format(i_idx) |
| i_idx += 1 |
| |
| p['IDELAY_ONLY'] = random.randint(0, 1) |
| p['DIFF_TERM'] = random.randint(0, 1) |
| if not p['IDELAY_ONLY']: |
| p['owire'] = luts.get_next_input_net() |
| else: |
| any_idelay = True |
| p['owire'] = 'idelay_{site}'.format(**p) |
| |
| p['DRIVE'] = None |
| p['SLEW'] = None |
| p['IBUF_LOW_PWR'] = random.randint(0, 1) |
| |
| elif p['type'] == 'OBUF': |
| p['pad_wire'] = 'do[{}]'.format(o_idx) |
| p['iwire'] = luts.get_next_output_net() |
| if drives is not None: |
| p['DRIVE'] = random.choice(drives) |
| else: |
| p['DRIVE'] = None |
| p['SLEW'] = verilog.quote(random.choice(slews)) |
| |
| o_idx += 1 |
| elif p['type'] == 'OBUFDS': |
| p['pad_wire'] = 'do[{}]'.format(o_idx) |
| o_idx += 1 |
| p['bpad_wire'] = 'do[{}]'.format(o_idx) |
| o_idx += 1 |
| p['iwire'] = luts.get_next_output_net() |
| if drives is not None: |
| p['DRIVE'] = random.choice(drives) |
| else: |
| p['DRIVE'] = None |
| p['SLEW'] = verilog.quote(random.choice(slews)) |
| elif p['type'] == 'OBUFTDS': |
| p['pad_wire'] = 'do[{}]'.format(o_idx) |
| o_idx += 1 |
| p['bpad_wire'] = 'do[{}]'.format(o_idx) |
| o_idx += 1 |
| p['tristate_wire'] = random.choice( |
| ('0', luts.get_next_output_net())) |
| p['iwire'] = luts.get_next_output_net() |
| if drives is not None: |
| p['DRIVE'] = random.choice(drives) |
| else: |
| p['DRIVE'] = None |
| p['SLEW'] = verilog.quote(random.choice(slews)) |
| elif p['type'] == 'IOBUF_INTERMDISABLE': |
| p['pad_wire'] = 'dio[{}]'.format(io_idx) |
| p['iwire'] = luts.get_next_output_net() |
| p['owire'] = luts.get_next_input_net() |
| if drives is not None: |
| p['DRIVE'] = random.choice(drives) |
| else: |
| p['DRIVE'] = None |
| p['SLEW'] = verilog.quote(random.choice(slews)) |
| p['tristate_wire'] = random.choice( |
| ('0', luts.get_next_output_net())) |
| p['ibufdisable_wire'] = random.choice( |
| ('0', luts.get_next_output_net())) |
| p['intermdisable_wire'] = random.choice( |
| ('0', luts.get_next_output_net())) |
| io_idx += 1 |
| |
| if 'DRIVE' in p: |
| if p['DRIVE'] is not None: |
| p['DRIVE_STR'] = '.DRIVE({}),'.format(p['DRIVE']) |
| else: |
| p['DRIVE_STR'] = '' |
| |
| if 'SLEW' in p: |
| p['SLEW_STR'] = '' |
| if iostandard in only_diff_map: |
| p['SLEW'] = None |
| elif p['DRIVE'] is not None: |
| p['SLEW_STR'] = '.SLEW({}),'.format(p['SLEW']) |
| |
| if p['type'] is not None: |
| tile_params.append( |
| ( |
| tile, |
| site, |
| p['pad_wire'], |
| iostandard_site, |
| p['DRIVE'], |
| verilog.unquote(p['SLEW']) if p['SLEW'] else None, |
| verilog.unquote(p['PULLTYPE']), |
| p['IN_TERM'] if 'IN_TERM' in p else None, |
| )) |
| params['tiles'].append(p) |
| |
| write_params(tile_params) |
| |
| with open('iobank_vref.csv', 'w') as f: |
| for iobank, vref in params['INTERNAL_VREF'].items(): |
| f.write('{},{}\n'.format(iobank, vref)) |
| |
| print( |
| ''' |
| `define N_DI {n_di} |
| `define N_DO {n_do} |
| `define N_DIO {n_dio} |
| |
| module top(input wire [`N_DI-1:0] di, output wire [`N_DO-1:0] do, inout wire [`N_DIO-1:0] dio); |
| '''.format(n_di=i_idx, n_do=o_idx, n_dio=io_idx)) |
| |
| if any_idelay: |
| print(''' |
| (* KEEP, DONT_TOUCH *) |
| IDELAYCTRL();''') |
| |
| # Always output a LUT6 to make placer happy. |
| print(''' |
| (* KEEP, DONT_TOUCH *) |
| LUT6 dummy_lut();''') |
| |
| for p in params['tiles']: |
| if p['type'] is None: |
| continue |
| elif p['type'] == 'IBUF': |
| print( |
| ''' |
| wire idelay_{site}; |
| |
| (* KEEP, DONT_TOUCH *) |
| IBUF #( |
| .IBUF_LOW_PWR({IBUF_LOW_PWR}), |
| .IOSTANDARD({IOSTANDARD}) |
| ) ibuf_{site} ( |
| .I({pad_wire}), |
| .O({owire}) |
| );'''.format(**p), |
| file=connects) |
| if p['IDELAY_ONLY']: |
| print( |
| """ |
| (* KEEP, DONT_TOUCH *) |
| IDELAYE2 idelay_site_{site} ( |
| .IDATAIN(idelay_{site}) |
| );""".format(**p), |
| file=connects) |
| |
| elif p['type'] == 'IBUFDS': |
| print( |
| ''' |
| wire idelay_{site}; |
| |
| (* KEEP, DONT_TOUCH *) |
| IBUFDS #( |
| .IBUF_LOW_PWR({IBUF_LOW_PWR}), |
| .DIFF_TERM({DIFF_TERM}), |
| .IOSTANDARD({IOSTANDARD}) |
| ) ibuf_{site} ( |
| .I({pad_wire}), |
| .IB({bpad_wire}), |
| .O({owire}) |
| );'''.format(**p), |
| file=connects) |
| if p['IDELAY_ONLY']: |
| print( |
| """ |
| (* KEEP, DONT_TOUCH *) |
| IDELAYE2 idelay_site_{site} ( |
| .IDATAIN(idelay_{site}) |
| );""".format(**p), |
| file=connects) |
| |
| elif p['type'] == 'OBUF': |
| print( |
| ''' |
| (* KEEP, DONT_TOUCH *) |
| OBUF #( |
| {DRIVE_STR} |
| {SLEW_STR} |
| .IOSTANDARD({IOSTANDARD}) |
| ) obuf_{site} ( |
| .O({pad_wire}), |
| .I({iwire}) |
| );'''.format(**p), |
| file=connects) |
| elif p['type'] == 'OBUFDS': |
| print( |
| ''' |
| (* KEEP, DONT_TOUCH *) |
| OBUFDS #( |
| {DRIVE_STR} |
| {SLEW_STR} |
| .IOSTANDARD({IOSTANDARD}) |
| ) obufds_{site} ( |
| .O({pad_wire}), |
| .OB({bpad_wire}), |
| .I({iwire}) |
| );'''.format(**p), |
| file=connects) |
| elif p['type'] == 'OBUFTDS': |
| print( |
| ''' |
| (* KEEP, DONT_TOUCH *) |
| OBUFTDS #( |
| {DRIVE_STR} |
| {SLEW_STR} |
| .IOSTANDARD({IOSTANDARD}) |
| ) obufds_{site} ( |
| .O({pad_wire}), |
| .OB({bpad_wire}), |
| .T({tristate_wire}), |
| .I({iwire}) |
| );'''.format(**p), |
| file=connects) |
| elif p['type'] == 'IOBUF_INTERMDISABLE': |
| print( |
| ''' |
| (* KEEP, DONT_TOUCH *) |
| IOBUF_INTERMDISABLE #( |
| {DRIVE_STR} |
| {SLEW_STR} |
| .IOSTANDARD({IOSTANDARD}) |
| ) ibuf_{site} ( |
| .IO({pad_wire}), |
| .I({iwire}), |
| .O({owire}), |
| .T({tristate_wire}), |
| .IBUFDISABLE({ibufdisable_wire}), |
| .INTERMDISABLE({intermdisable_wire}) |
| );'''.format(**p), |
| file=connects) |
| |
| for l in luts.create_wires_and_luts(): |
| print(l) |
| |
| print(connects.getvalue()) |
| |
| print("endmodule") |
| |
| with open('params.json', 'w') as f: |
| json.dump(params, f, indent=2) |
| |
| |
| if __name__ == '__main__': |
| run() |