mirror of https://github.com/openXC7/prjxray.git
437 lines
13 KiB
Python
437 lines
13 KiB
Python
#!/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 os
|
|
import random
|
|
random.seed(int(os.getenv("SEED"), 16))
|
|
from prjxray import util
|
|
from prjxray.db import Database
|
|
|
|
|
|
class PipList(object):
|
|
def __init__(self):
|
|
self.piplist = {}
|
|
self.ppiplist = {}
|
|
|
|
def get_pip_and_ppip_list_for_tile_type(self, tile_type):
|
|
|
|
# Load PIP list for the tile type if not already loaded
|
|
if tile_type not in self.piplist:
|
|
self.piplist[tile_type] = []
|
|
|
|
fname = os.path.join(
|
|
os.getenv('FUZDIR'), '..', 'piplist', 'build', 'cmt_top_lower',
|
|
tile_type.lower() + '.txt')
|
|
|
|
with open(fname, "r") as f:
|
|
for l in f:
|
|
pip, is_directional = l.strip().split(' ')
|
|
tile, dst, src = pip.split('.')
|
|
if tile_type == tile:
|
|
self.piplist[tile_type].append(
|
|
(src, dst, bool(int(is_directional))))
|
|
|
|
# Load PPIP list for the tile type if not already loaded
|
|
if tile_type not in self.ppiplist:
|
|
self.ppiplist[tile_type] = []
|
|
|
|
fname = os.path.join(
|
|
os.getenv('FUZDIR'), '..', '071-ppips', 'build',
|
|
'ppips_' + tile_type.lower() + '.db')
|
|
|
|
with open(fname, "r") as f:
|
|
for l in f:
|
|
pip_data, pip_type = l.strip().split()
|
|
|
|
if pip_type != 'always':
|
|
continue
|
|
|
|
tile, dst, src = pip_data.strip().split('.')
|
|
if tile_type == tile:
|
|
self.ppiplist[tile_type].append((src, dst, True))
|
|
|
|
return self.piplist[tile_type], self.ppiplist[tile_type]
|
|
|
|
|
|
def find_phasers_for_mmcm(grid, loc):
|
|
gridinfo = grid.gridinfo_at_loc((loc[0], loc[1] - 9))
|
|
|
|
phasers = {
|
|
'IN': [],
|
|
'OUT': [],
|
|
}
|
|
|
|
for site_name, site_type in gridinfo.sites.items():
|
|
if site_type == 'PHASER_IN_PHY':
|
|
phasers['IN'].append(site_name)
|
|
elif site_type == 'PHASER_OUT_PHY':
|
|
phasers['OUT'].append(site_name)
|
|
|
|
assert len(phasers['IN']) > 0
|
|
assert len(phasers['OUT']) > 0
|
|
|
|
phasers['IN'].sort()
|
|
phasers['OUT'].sort()
|
|
|
|
return phasers
|
|
|
|
|
|
def find_hclk_ref_wires_for_mmcm(grid, loc):
|
|
tilename = grid.tilename_at_loc((loc[0], loc[1] - 17))
|
|
gridinfo = grid.gridinfo_at_tilename(tilename)
|
|
|
|
assert gridinfo.tile_type in ['HCLK_CMT_L', 'HCLK_CMT']
|
|
|
|
# HCLK_CMT_MUX_OUT_FREQ_REF[0-3]
|
|
wires = []
|
|
for idx in range(4):
|
|
wires.append('{}/HCLK_CMT_MUX_OUT_FREQ_REF{}'.format(tilename, idx))
|
|
|
|
return wires
|
|
|
|
|
|
def gen_sites():
|
|
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)
|
|
|
|
for site_name, site_type in gridinfo.sites.items():
|
|
if site_type in ['MMCME2_ADV']:
|
|
phasers = find_phasers_for_mmcm(grid, loc)
|
|
hclk_wires = find_hclk_ref_wires_for_mmcm(grid, loc)
|
|
yield tile_name, site_name, phasers, hclk_wires
|
|
|
|
|
|
def get_random_route_from_site_pin(
|
|
pip_list, tile_name, site_pin, direction, occupied_wires, hclk_wires):
|
|
|
|
# A map of MMCM site pins to wires they are connected to.
|
|
pin_to_wire = {
|
|
"CMT_TOP_L_LOWER_B": {
|
|
"CLKIN1": "CMT_LR_LOWER_B_MMCM_CLKIN1",
|
|
"CLKIN2": "CMT_LR_LOWER_B_MMCM_CLKIN2",
|
|
"CLKFBIN": "CMT_LR_LOWER_B_MMCM_CLKFBIN",
|
|
"CLKOUT0": "CMT_LR_LOWER_B_MMCM_CLKOUT0",
|
|
"CLKOUT1": "CMT_LR_LOWER_B_MMCM_CLKOUT1",
|
|
"CLKOUT2": "CMT_LR_LOWER_B_MMCM_CLKOUT2",
|
|
"CLKOUT3": "CMT_LR_LOWER_B_MMCM_CLKOUT3",
|
|
"CLKOUT4": "CMT_LR_LOWER_B_MMCM_CLKOUT4",
|
|
"CLKOUT5": "CMT_LR_LOWER_B_MMCM_CLKOUT5",
|
|
"CLKOUT6": "CMT_LR_LOWER_B_MMCM_CLKOUT6",
|
|
},
|
|
"CMT_TOP_R_LOWER_B": {
|
|
"CLKIN1": "CMT_LR_LOWER_B_MMCM_CLKIN1",
|
|
"CLKIN2": "CMT_LR_LOWER_B_MMCM_CLKIN2",
|
|
"CLKFBIN": "CMT_LR_LOWER_B_MMCM_CLKFBIN",
|
|
"CLKOUT0": "CMT_LR_LOWER_B_MMCM_CLKOUT0",
|
|
"CLKOUT1": "CMT_LR_LOWER_B_MMCM_CLKOUT1",
|
|
"CLKOUT2": "CMT_LR_LOWER_B_MMCM_CLKOUT2",
|
|
"CLKOUT3": "CMT_LR_LOWER_B_MMCM_CLKOUT3",
|
|
"CLKOUT4": "CMT_LR_LOWER_B_MMCM_CLKOUT4",
|
|
"CLKOUT5": "CMT_LR_LOWER_B_MMCM_CLKOUT5",
|
|
"CLKOUT6": "CMT_LR_LOWER_B_MMCM_CLKOUT6",
|
|
},
|
|
}
|
|
|
|
# Get tile type
|
|
tile_type = tile_name.rsplit("_", maxsplit=1)[0]
|
|
|
|
# Get all PIPs (PIPs + PPIPs)
|
|
pips, ppips = pip_list.get_pip_and_ppip_list_for_tile_type(tile_type)
|
|
all_pips = pips + ppips
|
|
|
|
# The first wire
|
|
wire = pin_to_wire[tile_type][site_pin]
|
|
|
|
if site_pin in ["CLKIN1", "CLKIN2", "CLKFBIN"] and random.random() < .2:
|
|
return [random.choice(hclk_wires), wire]
|
|
|
|
# Walk randomly.
|
|
route = []
|
|
while True:
|
|
route.append(wire)
|
|
|
|
wires = []
|
|
|
|
for src, dst, is_directional in all_pips:
|
|
if direction == "down" and src == wire:
|
|
next_wire = dst
|
|
elif direction == "down" and dst == wire and not is_directional:
|
|
next_wire = src
|
|
elif direction == "up" and dst == wire:
|
|
next_wire = src
|
|
elif direction == "up" and src == wire and not is_directional:
|
|
next_wire = dst
|
|
else:
|
|
continue
|
|
|
|
if next_wire not in occupied_wires:
|
|
wires.append(next_wire)
|
|
|
|
if len(wires) == 0:
|
|
break
|
|
|
|
wire = random.choice(wires)
|
|
occupied_wires.add(wire)
|
|
|
|
# For "up" direction reverse the route.
|
|
if direction == "down":
|
|
return route
|
|
if direction == "up":
|
|
return route[::-1]
|
|
|
|
|
|
def main():
|
|
|
|
# 8 inputs per clock region
|
|
# 5 clock regions for device
|
|
max_clk_inputs = 8 * 5
|
|
clkin_idx = 0
|
|
|
|
print(
|
|
'''
|
|
module top(
|
|
input wire [{nclkin}:0] clkin
|
|
);
|
|
|
|
(* KEEP, DONT_TOUCH *)
|
|
LUT6 dummy();
|
|
'''.format(nclkin=max_clk_inputs - 1))
|
|
|
|
pip_list = PipList()
|
|
bufg_count = 0
|
|
|
|
design_file = open('design.txt', 'w')
|
|
routes_file = open('routes.txt', 'w')
|
|
|
|
count = 0
|
|
mmcms = sorted(gen_sites(), key=lambda x: x[0])
|
|
random.shuffle(mmcms)
|
|
for tile, site, phasers, hclk_wires in mmcms:
|
|
in_use = random.randint(0, 2) > 0
|
|
count += 1
|
|
|
|
design_file.write("{},{},{}\n".format(tile, site, int(in_use)))
|
|
|
|
if not in_use:
|
|
continue
|
|
|
|
# Generate random routes to/from some pins
|
|
routes = {}
|
|
endpoints = {}
|
|
|
|
pins = [
|
|
('CLKIN1', 'up'),
|
|
('CLKIN2', 'up'),
|
|
('CLKFBIN', 'up'),
|
|
|
|
# Sometimes manually randomized route for CLKOUTx conflicts with
|
|
# the verilog design.
|
|
#('CLKOUT0', 'down'),
|
|
#('CLKOUT1', 'down'),
|
|
#('CLKOUT2', 'down'),
|
|
#('CLKOUT3', 'down'),
|
|
#('CLKOUT4', 'down'),
|
|
#('CLKOUT5', 'down'),
|
|
#('CLKOUT6', 'down'),
|
|
]
|
|
|
|
occupied_wires = set()
|
|
for pin, dir in pins:
|
|
|
|
route = get_random_route_from_site_pin(
|
|
pip_list, tile, pin, dir, occupied_wires, hclk_wires)
|
|
if route is None:
|
|
endpoints[pin] = ""
|
|
continue
|
|
|
|
routes[pin] = (
|
|
route,
|
|
dir,
|
|
)
|
|
endpoints[pin] = route[-1] if dir == 'down' else route[0]
|
|
|
|
internal_feedback = endpoints['CLKFBIN'].endswith('CLKFBOUT')
|
|
|
|
# Store them in a random order so the TCL script will try to route
|
|
# them also in the random order.
|
|
lines = []
|
|
pins = sorted(routes.keys())
|
|
random.shuffle(pins)
|
|
for pin in pins:
|
|
(route, dir) = routes[pin]
|
|
route_str = " ".join(route)
|
|
lines.append(
|
|
'{} {} {} {} {}\n'.format(tile, site, pin, dir, route_str))
|
|
|
|
random.shuffle(lines)
|
|
routes_file.writelines(lines)
|
|
|
|
clkfbin_src = random.choice(('BUFH', 'logic'))
|
|
|
|
if internal_feedback:
|
|
COMPENSATION = "INTERNAL"
|
|
else:
|
|
if clkfbin_src == 'logic':
|
|
COMPENSATION = 'EXTERNAL'
|
|
else:
|
|
COMPENSATION = "ZHOLD"
|
|
|
|
print(
|
|
"""
|
|
wire clkfbin_{site};
|
|
wire clkin1_{site};
|
|
wire clkin2_{site};
|
|
wire clkfbout_mult_{site};
|
|
wire clkout0_{site};
|
|
wire clkout1_{site};
|
|
wire clkout2_{site};
|
|
wire clkout3_{site};
|
|
wire clkout4_{site};
|
|
wire clkout5_{site};
|
|
(* KEEP, DONT_TOUCH, LOC = "{site}" *)
|
|
MMCME2_ADV #(
|
|
.COMPENSATION("{COMPENSATION}")
|
|
) mmcm_{site} (
|
|
.CLKFBOUT(clkfbout_mult_{site}),
|
|
.CLKOUT0(clkout0_{site}),
|
|
.CLKOUT1(clkout1_{site}),
|
|
.CLKOUT2(clkout2_{site}),
|
|
.CLKOUT3(clkout3_{site}),
|
|
.CLKOUT4(clkout4_{site}),
|
|
.CLKOUT5(clkout5_{site}),
|
|
.DRDY(),
|
|
.LOCKED(),
|
|
.DO(),
|
|
.CLKFBIN(clkfbin_{site}),
|
|
.CLKIN1(clkin1_{site}),
|
|
.CLKIN2(clkin2_{site}),
|
|
.CLKINSEL(),
|
|
.DCLK(),
|
|
.DEN(),
|
|
.DWE(),
|
|
.PWRDWN(),
|
|
.RST(),
|
|
.DI(),
|
|
.DADDR());
|
|
""".format(site=site, COMPENSATION=COMPENSATION))
|
|
|
|
for clkout in range(4, 6):
|
|
# CLKOUT4 and CLKOUT5 can only drive one signal type
|
|
if random.randint(0, 1) and bufg_count < 16:
|
|
bufg_count += 1
|
|
print(
|
|
"""
|
|
(* KEEP, DONT_TOUCH *)
|
|
BUFG (
|
|
.I(clkout{idx}_{site})
|
|
);""".format(idx=clkout, site=site))
|
|
|
|
any_phaser = False
|
|
|
|
for clkout in range(4):
|
|
# CLKOUT0-CLKOUT3 can drive:
|
|
# - Global drivers (e.g. BUFG)
|
|
# - PHASER_[IN|OUT]_[CA|DB]_FREQREFCLK via BB_[0-3]
|
|
drive_bufg = random.randint(0, 1) and bufg_count < 16
|
|
drive_phaser = 0 #random.randint(0, 1)
|
|
|
|
if drive_bufg:
|
|
bufg_count += 1
|
|
print(
|
|
"""
|
|
(* KEEP, DONT_TOUCH *)
|
|
BUFG (
|
|
.I(clkout{idx}_{site})
|
|
);""".format(idx=clkout, site=site))
|
|
|
|
if drive_phaser and not any_phaser and False:
|
|
any_phaser = True
|
|
print(
|
|
"""
|
|
(* KEEP, DONT_TOUCH, LOC="{phaser_loc}" *)
|
|
PHASER_OUT phaser_{site}(
|
|
.FREQREFCLK(clkout{idx}_{site})
|
|
);""".format(idx=clkout, site=site, phaser_loc=phasers['OUT'][0]))
|
|
|
|
if internal_feedback:
|
|
print(
|
|
"""
|
|
assign clkfbin_{site} = clkfbout_mult_{site};
|
|
""".format(site=site))
|
|
else:
|
|
if clkfbin_src == 'BUFH':
|
|
print(
|
|
"""
|
|
(* KEEP, DONT_TOUCH *)
|
|
BUFH (
|
|
.I(clkfbout_mult_{site}),
|
|
.O(clkfbin_{site})
|
|
);""".format(site=site))
|
|
elif clkfbin_src == 'logic':
|
|
print(
|
|
"""
|
|
(* KEEP, DONT_TOUCH *)
|
|
LUT6 # (.INIT(64'h5555555555555555))
|
|
clkfbin_logic_{site} (
|
|
.I0(clkfbout_mult_{site}),
|
|
.O(clkfbin_{site})
|
|
);
|
|
""".format(site=site))
|
|
else:
|
|
assert False, clkfbin_src
|
|
|
|
for clkin in range(2):
|
|
clkin_src = random.choice((
|
|
'BUFH',
|
|
'BUFR',
|
|
'logic',
|
|
))
|
|
|
|
if clkin_src == 'BUFH':
|
|
print(
|
|
"""
|
|
(* KEEP, DONT_TOUCH *)
|
|
BUFH (
|
|
.O(clkin{idx}_{site}),
|
|
.I(clkin{idx2})
|
|
);""".format(idx=clkin + 1, idx2=clkin_idx, site=site))
|
|
elif clkin_src == 'BUFR':
|
|
print(
|
|
"""
|
|
(* KEEP, DONT_TOUCH *)
|
|
BUFR (
|
|
.O(clkin{idx}_{site}),
|
|
.I(clkin{idx2})
|
|
);""".format(idx=clkin + 1, idx2=clkin_idx, site=site))
|
|
elif clkin_src == 'logic':
|
|
print(
|
|
"""
|
|
(* KEEP, DONT_TOUCH *)
|
|
LUT6 # (.INIT(64'h5555555555555555))
|
|
clkin{idx}_logic_{site} (
|
|
.I0(clkin{idx2}),
|
|
.O(clkin{idx}_{site})
|
|
);
|
|
""".format(idx=clkin + 1, idx2=clkin_idx, site=site))
|
|
else:
|
|
assert False, (clkin, clkin_src)
|
|
|
|
clkin_idx += 1
|
|
|
|
print("endmodule")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|