mirror of https://github.com/openXC7/prjxray.git
429 lines
13 KiB
Python
429 lines
13 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (C) 2017-2022 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
|
|
|
|
from iostandards import *
|
|
|
|
def gen_sites():
|
|
'''
|
|
IOB18S: main IOB of a diff pair
|
|
IOB18M: secondary IOB of a diff pair
|
|
IOB18: not a diff pair. Relatively rare (at least in ROI...2 of them?)
|
|
Focus on IOB18S 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 ['IOB18S', 'IOB18M']:
|
|
sites[site_type] = site_name
|
|
|
|
if sites:
|
|
yield tile_name, sites
|
|
|
|
|
|
def write_params(params):
|
|
pinstr = 'tile,site,pin,iostandard,drive,slew,pulltype\n'
|
|
for vals in params:
|
|
pinstr += ','.join(map(str, vals)) + '\n'
|
|
|
|
open('params.csv', 'w').write(pinstr)
|
|
|
|
|
|
def run():
|
|
tile_types = ['IBUF', 'OBUF', 'IOBUF_DCIEN', None, None]
|
|
|
|
i_idx = 0
|
|
o_idx = 0
|
|
io_idx = 0
|
|
|
|
iostandards = LVCMOS + SSTL + LVDS
|
|
|
|
diff_map = {
|
|
"SSTL15": DIFF_SSTL15,
|
|
"SSTL135": DIFF_SSTL135,
|
|
"SSTL12": DIFF_SSTL12,
|
|
}
|
|
|
|
vref_map = {
|
|
"SSTL12": .600,
|
|
"SSTL135": .675,
|
|
"SSTL15": .75,
|
|
"DIFF_SSTL12": .600,
|
|
"DIFF_SSTL135": .675,
|
|
"DIFF_SSTL15": .75,
|
|
}
|
|
|
|
only_diff_map = {
|
|
"LVDS": ["LVDS"],
|
|
}
|
|
|
|
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 SSTL:
|
|
params['INTERNAL_VREF'][iobank] = vref_map[iostandard]
|
|
|
|
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 ['LVCMOS12']:
|
|
drives = [2, 4, 6, 8]
|
|
elif iostandard in ['LVCMOS15', 'LVCMOS18']:
|
|
drives = [2, 4, 6, 8, 12, 16]
|
|
elif iostandard in LVDS + SSTL:
|
|
drives = None
|
|
else:
|
|
assert False, f"Unhandled iostandard: {iostandard}"
|
|
|
|
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['IOB18S'] = 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['IOB18S']
|
|
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)
|
|
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 iostandard in LVDS else 0
|
|
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_DCIEN':
|
|
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['dcitermdisable_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']),
|
|
))
|
|
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, input refclk);
|
|
'''.format(n_di=i_idx, n_do=o_idx, n_dio=io_idx))
|
|
|
|
if any_idelay:
|
|
print('''
|
|
(* KEEP, DONT_TOUCH *)
|
|
IDELAYCTRL(.REFCLK(refclk));''')
|
|
|
|
# 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_DCIEN':
|
|
print(
|
|
'''
|
|
(* KEEP, DONT_TOUCH *)
|
|
IOBUF_DCIEN #(
|
|
{DRIVE_STR}
|
|
{SLEW_STR}
|
|
.IOSTANDARD({IOSTANDARD})
|
|
) ibuf_{site} (
|
|
.IO({pad_wire}),
|
|
.I({iwire}),
|
|
.O({owire}),
|
|
.T({tristate_wire}),
|
|
.IBUFDISABLE({ibufdisable_wire}),
|
|
.DCITERMDISABLE({dcitermdisable_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()
|