mirror of https://github.com/openXC7/prjxray.git
392 lines
12 KiB
Python
392 lines
12 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 os
|
|
import random
|
|
import math
|
|
random.seed(int(os.getenv("SEED"), 16))
|
|
from prjxray import util
|
|
from prjxray import verilog
|
|
from prjxray import lut_maker
|
|
from prjxray.db import Database
|
|
|
|
NOT_INCLUDED_TILES = ['RIOI_SING']
|
|
|
|
SITE_TYPES = ['OLOGICE2', 'ILOGICE2']
|
|
|
|
|
|
def read_site_to_cmt():
|
|
""" Yields clock sources and which CMT they route within. """
|
|
with open(os.path.join(os.getenv('FUZDIR'), 'build',
|
|
'cmt_regions.csv')) as f:
|
|
for l in f:
|
|
site, cmt = l.strip().split(',')
|
|
yield (site, cmt)
|
|
|
|
|
|
def gen_sites():
|
|
''' Return dict of ISERDES/OSERDES locations. '''
|
|
db = Database(util.get_db_root(), util.get_part())
|
|
grid = db.grid()
|
|
|
|
xy_fun = util.create_xy_fun('\S+')
|
|
|
|
tiles = grid.tiles()
|
|
|
|
for tile_name in sorted(tiles):
|
|
loc = grid.loc_of_tilename(tile_name)
|
|
gridinfo = grid.gridinfo_at_loc(loc)
|
|
tile_type = gridinfo.tile_type
|
|
|
|
tile = {'tile': tile_name, 'tile_type': tile_type, 'ioi_sites': {}}
|
|
|
|
for site_name, site_type in gridinfo.sites.items():
|
|
if site_type in SITE_TYPES:
|
|
xy = xy_fun(site_name)
|
|
if xy not in tile['ioi_sites']:
|
|
tile['ioi_sites'][xy] = {}
|
|
|
|
tile['ioi_sites'][xy][site_type] = site_name
|
|
|
|
yield tile
|
|
|
|
|
|
class ClockSources(object):
|
|
def __init__(self):
|
|
self.site_to_cmt = dict(read_site_to_cmt())
|
|
|
|
self.leaf_gclks = {}
|
|
self.ioclks = {}
|
|
self.rclks = {}
|
|
self.selected_leaf_gclks = {}
|
|
self.lut_maker = lut_maker.LutMaker()
|
|
|
|
for cmt in set(self.site_to_cmt.values()):
|
|
self.leaf_gclks[cmt] = []
|
|
self.ioclks[cmt] = []
|
|
self.rclks[cmt] = []
|
|
|
|
def init_clocks(self):
|
|
""" Initialize all IOI clock sources. """
|
|
for site, cmt in self.site_to_cmt.items():
|
|
clk = 'clk_' + site
|
|
if 'BUFHCE' in site:
|
|
print(
|
|
"""
|
|
wire {clk};
|
|
(* KEEP, DONT_TOUCH, LOC = "{site}" *)
|
|
BUFH bufh_{site}(
|
|
.O({clk})
|
|
);
|
|
""".format(
|
|
clk=clk,
|
|
site=site,
|
|
))
|
|
|
|
self.leaf_gclks[cmt].append(clk)
|
|
|
|
if 'BUFIO' in site:
|
|
print(
|
|
"""
|
|
wire {clk};
|
|
(* KEEP, DONT_TOUCH, LOC = "{site}" *)
|
|
BUFIO bufio_{site}(
|
|
.O({clk})
|
|
);
|
|
""".format(
|
|
clk=clk,
|
|
site=site,
|
|
))
|
|
|
|
self.ioclks[cmt].append(clk)
|
|
|
|
if 'BUFR' in site:
|
|
print(
|
|
"""
|
|
wire {clk};
|
|
(* KEEP, DONT_TOUCH, LOC = "{site}" *)
|
|
BUFR bufr_{site}(
|
|
.O({clk})
|
|
);
|
|
""".format(
|
|
clk=clk,
|
|
site=site,
|
|
))
|
|
|
|
self.rclks[cmt].append(clk)
|
|
|
|
# Choose 6 leaf_gclks to be used in each CMT.
|
|
for cmt in self.leaf_gclks:
|
|
self.selected_leaf_gclks[cmt] = random.sample(
|
|
self.leaf_gclks[cmt], 6)
|
|
|
|
def get_clock(
|
|
self,
|
|
site,
|
|
allow_ioclks,
|
|
allow_rclks,
|
|
allow_fabric=True,
|
|
allow_empty=True):
|
|
cmt = self.site_to_cmt[site]
|
|
choices = []
|
|
if allow_fabric:
|
|
choices.append('lut')
|
|
|
|
if allow_empty:
|
|
choices.append('')
|
|
|
|
choices.extend(self.selected_leaf_gclks[cmt])
|
|
if allow_ioclks:
|
|
choices.extend(self.ioclks[cmt])
|
|
|
|
if allow_rclks:
|
|
choices.extend(self.rclks[cmt])
|
|
|
|
clock = random.choice(choices)
|
|
is_lut = False
|
|
if clock == "lut":
|
|
clock = self.lut_maker.get_next_output_net()
|
|
is_lut = True
|
|
return clock, is_lut
|
|
|
|
|
|
def add_port(ports, port, signal):
|
|
ports.append('.{}({})'.format(port, signal))
|
|
|
|
|
|
def run():
|
|
print("module top();")
|
|
|
|
clocks = ClockSources()
|
|
clocks.init_clocks()
|
|
"""
|
|
|
|
ISERDESE2 clock sources:
|
|
|
|
CLK/CLKB:
|
|
- Allows LEAF_GCLK, IOCLKS, RCLKS and fabric
|
|
- Dedicated pips
|
|
|
|
CLKDIV:
|
|
- No dedicated pips, uses fabric clock in.
|
|
|
|
CLKDIVP:
|
|
- Has pips, MIG only, PHASER or fabric.
|
|
|
|
OCLK/OCLKB:
|
|
- Allows LEAF_GCLK, IOCLKS, RCLKS and fabric
|
|
- Must match OSERDESE2:CLK/CLKB
|
|
|
|
OSERDESE2 clock sources:
|
|
|
|
CLKDIV/CLKDIVB:
|
|
- Allows LEAF_GCLK and RCLKS and fabric
|
|
- Dedicated pips
|
|
|
|
CLKDIVF/CLKDIVFB:
|
|
- Allows LEAF_GCLK and RCLKS and fabric
|
|
- No explicit port, follows CLKDIV/CLKDIVB?
|
|
"""
|
|
|
|
output = []
|
|
route_file = open("routes.txt", "w")
|
|
|
|
for tile in gen_sites():
|
|
if tile['tile_type'] in NOT_INCLUDED_TILES:
|
|
continue
|
|
|
|
for xy in tile['ioi_sites']:
|
|
ilogic_site_type = random.choice([None, 'ISERDESE2', 'IDDR'])
|
|
use_oserdes = random.randint(0, 1)
|
|
|
|
ilogic_site = tile['ioi_sites'][xy]['ILOGICE2']
|
|
ologic_site = tile['ioi_sites'][xy]['OLOGICE2']
|
|
|
|
if use_oserdes:
|
|
oclk, _ = clocks.get_clock(
|
|
ologic_site, allow_ioclks=True, allow_rclks=True)
|
|
|
|
oclkb = oclk
|
|
else:
|
|
oclk, is_lut = clocks.get_clock(
|
|
ilogic_site, allow_ioclks=True, allow_rclks=True)
|
|
|
|
if random.randint(0, 1):
|
|
oclkb = oclk
|
|
else:
|
|
if random.randint(0, 1):
|
|
oclkb, _ = clocks.get_clock(
|
|
ilogic_site,
|
|
allow_ioclks=True,
|
|
allow_rclks=True,
|
|
allow_fabric=not is_lut)
|
|
else:
|
|
# Explicitly provide IMUX stimulus to resolve IMUX pips
|
|
oclk = random.randint(0, 1)
|
|
oclkb = random.randint(0, 1)
|
|
|
|
DATA_RATE = random.choice(['DDR', 'SDR'])
|
|
clk, clk_is_lut = clocks.get_clock(
|
|
ilogic_site,
|
|
allow_ioclks=True,
|
|
allow_rclks=True,
|
|
allow_empty=DATA_RATE == 'SDR')
|
|
|
|
clkb = clk
|
|
while clkb == clk:
|
|
clkb, clkb_is_lut = clocks.get_clock(
|
|
ilogic_site,
|
|
allow_ioclks=True,
|
|
allow_rclks=True,
|
|
allow_empty=False)
|
|
|
|
imux_available = {
|
|
0: set(("IOI_IMUX20_0", "IOI_IMUX22_0")),
|
|
1: set(("IOI_IMUX20_1", "IOI_IMUX22_1")),
|
|
}
|
|
|
|
# Force CLK route through IMUX when connected to a LUT
|
|
if clk_is_lut:
|
|
y = (xy[1] + 1) % 2
|
|
|
|
route = random.choice(list(imux_available[y]))
|
|
imux_available[y].remove(route)
|
|
|
|
route = "{}/{}".format(tile["tile"], route)
|
|
route_file.write("{} {}\n".format(clk, route))
|
|
|
|
# Force CLKB route through IMUX when connected to a LUT
|
|
if clkb_is_lut:
|
|
y = (xy[1] + 1) % 2
|
|
|
|
route = random.choice(list(imux_available[y]))
|
|
imux_available[y].remove(route)
|
|
|
|
route = "{}/{}".format(tile["tile"], route)
|
|
route_file.write("{} {}\n".format(clkb, route))
|
|
|
|
if ilogic_site_type is None:
|
|
pass
|
|
|
|
elif ilogic_site_type == 'ISERDESE2':
|
|
INTERFACE_TYPE = random.choice(
|
|
[
|
|
'MEMORY',
|
|
'MEMORY_DDR3',
|
|
'MEMORY_QDR',
|
|
'NETWORKING',
|
|
'OVERSAMPLE',
|
|
])
|
|
ports = []
|
|
|
|
add_port(ports, 'CLK', clk)
|
|
add_port(ports, 'CLKB', clkb)
|
|
add_port(ports, 'OCLK', oclk)
|
|
add_port(ports, 'OCLKB', oclkb)
|
|
|
|
output.append(
|
|
"""
|
|
(* KEEP, DONT_TOUCH, LOC="{site}" *)
|
|
ISERDESE2 #(
|
|
.DATA_RATE({DATA_RATE}),
|
|
.INTERFACE_TYPE({INTERFACE_TYPE}),
|
|
.IS_CLK_INVERTED({IS_CLK_INVERTED}),
|
|
.IS_CLKB_INVERTED({IS_CLKB_INVERTED}),
|
|
.IS_OCLK_INVERTED({IS_OCLK_INVERTED}),
|
|
.IS_OCLKB_INVERTED({IS_OCLKB_INVERTED}),
|
|
.INIT_Q1({INIT_Q1}),
|
|
.INIT_Q2({INIT_Q2}),
|
|
.INIT_Q3({INIT_Q3}),
|
|
.INIT_Q4({INIT_Q4}),
|
|
.SRVAL_Q1({SRVAL_Q1}),
|
|
.SRVAL_Q2({SRVAL_Q2}),
|
|
.SRVAL_Q3({SRVAL_Q3}),
|
|
.SRVAL_Q4({SRVAL_Q4})
|
|
) iserdes_{site}(
|
|
{ports});""".format(
|
|
site=ilogic_site,
|
|
ports=',\n'.join(ports),
|
|
DATA_RATE=verilog.quote(DATA_RATE),
|
|
INTERFACE_TYPE=verilog.quote(INTERFACE_TYPE),
|
|
IS_CLK_INVERTED=random.randint(0, 1),
|
|
IS_CLKB_INVERTED=random.randint(0, 1),
|
|
IS_OCLK_INVERTED=random.randint(0, 1),
|
|
IS_OCLKB_INVERTED=random.randint(0, 1),
|
|
INIT_Q1=random.randint(0, 1),
|
|
INIT_Q2=random.randint(0, 1),
|
|
INIT_Q3=random.randint(0, 1),
|
|
INIT_Q4=random.randint(0, 1),
|
|
SRVAL_Q1=random.randint(0, 1),
|
|
SRVAL_Q2=random.randint(0, 1),
|
|
SRVAL_Q3=random.randint(0, 1),
|
|
SRVAL_Q4=random.randint(0, 1),
|
|
))
|
|
elif ilogic_site_type == 'IDDR':
|
|
ports = []
|
|
add_port(ports, 'C', clk)
|
|
add_port(ports, 'CB', clkb)
|
|
|
|
output.append(
|
|
"""
|
|
(* KEEP, DONT_TOUCH, LOC="{site}" *)
|
|
IDDR_2CLK #(
|
|
.INIT_Q1({INIT_Q1}),
|
|
.INIT_Q2({INIT_Q2}),
|
|
.SRTYPE({SRTYPE})
|
|
) iserdes_{site}(
|
|
{ports});""".format(
|
|
site=ilogic_site,
|
|
ports=',\n'.join(ports),
|
|
INIT_Q1=random.randint(0, 1),
|
|
INIT_Q2=random.randint(0, 1),
|
|
SRTYPE=verilog.quote(random.choice(['ASYNC', 'SYNC'])),
|
|
))
|
|
else:
|
|
assert False, ilogic_site_type
|
|
|
|
if use_oserdes:
|
|
ports = []
|
|
|
|
add_port(
|
|
ports, 'CLKDIV',
|
|
clocks.get_clock(
|
|
ologic_site,
|
|
allow_ioclks=False,
|
|
allow_rclks=True,
|
|
)[0])
|
|
|
|
add_port(ports, 'CLK', oclk)
|
|
|
|
output.append(
|
|
"""
|
|
(* KEEP, DONT_TOUCH, LOC = "{site}" *)
|
|
OSERDESE2 #(
|
|
.IS_CLK_INVERTED({IS_CLK_INVERTED}),
|
|
.DATA_RATE_OQ("SDR"),
|
|
.DATA_RATE_TQ("SDR")
|
|
) oserdes_{site} (
|
|
{ports});""".format(
|
|
IS_CLK_INVERTED=random.randint(0, 1),
|
|
site=ologic_site,
|
|
ports=',\n'.join(ports),
|
|
))
|
|
|
|
for s in clocks.lut_maker.create_wires_and_luts():
|
|
print(s)
|
|
|
|
for s in output:
|
|
print(s)
|
|
|
|
print("endmodule")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
run()
|