blob: 6944e1ca3ebdcc92e30edc61ee49d5b8f53f5b14 [file] [log] [blame]
#!/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()