mirror of https://github.com/openXC7/prjxray.git
514 lines
14 KiB
Python
514 lines
14 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 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)
|
|
|
|
for site_name, site_type in gridinfo.sites.items():
|
|
if site_type in ['IOB33S', 'IOB33M']:
|
|
yield tile_name, site_name
|
|
|
|
|
|
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 use_iserdese2(p, luts, connects):
|
|
iobdelay = random.choice((
|
|
'NONE',
|
|
'BOTH',
|
|
'IBUF',
|
|
'IFD',
|
|
))
|
|
|
|
p['IOBDELAY'] = verilog.quote(iobdelay)
|
|
p['INIT_Q1'] = random.randint(0, 1)
|
|
p['INIT_Q2'] = random.randint(0, 1)
|
|
p['INIT_Q3'] = random.randint(0, 1)
|
|
p['INIT_Q4'] = random.randint(0, 1)
|
|
|
|
p['SRVAL_Q1'] = random.randint(0, 1)
|
|
p['SRVAL_Q2'] = random.randint(0, 1)
|
|
p['SRVAL_Q3'] = random.randint(0, 1)
|
|
p['SRVAL_Q4'] = random.randint(0, 1)
|
|
p['NUM_CE'] = random.randint(1, 2)
|
|
|
|
p['IS_CLK_INVERTED'] = random.randint(0, 1)
|
|
p['IS_CLKB_INVERTED'] = random.randint(0, 1)
|
|
p['IS_OCLK_INVERTED'] = random.randint(0, 1)
|
|
p['IS_OCLKB_INVERTED'] = random.randint(0, 1)
|
|
p['IS_CLKDIV_INVERTED'] = random.randint(0, 1)
|
|
p['IS_D_INVERTED'] = random.randint(0, 1)
|
|
p['INTERFACE_TYPE'] = verilog.quote(
|
|
random.choice(
|
|
(
|
|
'MEMORY',
|
|
'MEMORY_DDR3',
|
|
'MEMORY_QDR',
|
|
'NETWORKING',
|
|
'OVERSAMPLE',
|
|
)))
|
|
p['DATA_RATE'] = verilog.quote(random.choice((
|
|
'SDR',
|
|
'DDR',
|
|
)))
|
|
if verilog.unquote(p['DATA_RATE']) == 'SDR':
|
|
data_widths = [2, 3, 4, 5, 6, 7, 8]
|
|
else:
|
|
data_widths = [4, 6, 8]
|
|
|
|
p['DATA_WIDTH'] = random.choice(data_widths)
|
|
p['SERDES_MODE'] = verilog.quote(random.choice(('MASTER', 'SLAVE')))
|
|
|
|
use_delay = iobdelay != 'NONE'
|
|
|
|
if iobdelay == 'NONE':
|
|
p['mux_config'] = 'direct'
|
|
p['iddr_mux_config'] = 'direct'
|
|
elif iobdelay == 'BOTH':
|
|
p['mux_config'] = 'idelay'
|
|
p['iddr_mux_config'] = 'idelay'
|
|
elif iobdelay == 'IBUF':
|
|
p['mux_config'] = 'idelay'
|
|
p['iddr_mux_config'] = 'direct'
|
|
elif iobdelay == 'IFD':
|
|
p['mux_config'] = 'direct'
|
|
p['iddr_mux_config'] = 'idelay'
|
|
|
|
p['OFB_USED'] = verilog.quote(random.choice(('TRUE', 'FALSE')))
|
|
p['DYN_CLKDIV_INV_EN'] = verilog.quote(random.choice(('TRUE', 'FALSE')))
|
|
p['DYN_CLK_INV_EN'] = verilog.quote(random.choice(('TRUE', 'FALSE')))
|
|
|
|
if use_delay:
|
|
print(
|
|
"""
|
|
wire idelay_{site};
|
|
|
|
(* KEEP, DONT_TOUCH, LOC = "{idelay_loc}" *)
|
|
IDELAYE2 #(
|
|
) idelay_site_{site} (
|
|
.IDATAIN({iwire}),
|
|
.DATAOUT(idelay_{site})
|
|
);""".format(**p),
|
|
file=connects)
|
|
|
|
p['ddly_connection'] = '.DDLY(idelay_{site}),'.format(**p)
|
|
else:
|
|
p['ddly_connection'] = ''
|
|
|
|
if verilog.unquote(p['OFB_USED']) == 'TRUE':
|
|
p['ODATA_RATE'] = verilog.quote(random.choice((
|
|
'SDR',
|
|
'DDR',
|
|
)))
|
|
if verilog.unquote(p['ODATA_RATE']) == 'SDR':
|
|
data_widths = [2, 3, 4, 5, 6, 7, 8]
|
|
else:
|
|
data_widths = [4, 6, 8]
|
|
|
|
p['ODATA_WIDTH'] = random.choice(data_widths)
|
|
p['OSERDES_MODE'] = verilog.quote(random.choice(('MASTER', 'SLAVE')))
|
|
|
|
if p['ODATA_WIDTH'] == 4 and verilog.unquote(p['ODATA_RATE']) == 'DDR':
|
|
p['TRISTATE_WIDTH'] = 4
|
|
else:
|
|
p['TRISTATE_WIDTH'] = 1
|
|
|
|
print(
|
|
"""
|
|
wire tfb_{site};
|
|
wire ofb_{site};
|
|
|
|
(* KEEP, DONT_TOUCH, LOC = "{ologic_loc}" *)
|
|
OSERDESE2 #(
|
|
.SERDES_MODE({OSERDES_MODE}),
|
|
.DATA_RATE_TQ({ODATA_RATE}),
|
|
.DATA_RATE_OQ({ODATA_RATE}),
|
|
.DATA_WIDTH({ODATA_WIDTH}),
|
|
.TRISTATE_WIDTH({TRISTATE_WIDTH})
|
|
) oserdese2_{site} (
|
|
.CLK(0),
|
|
.CLKDIV(0),
|
|
.D1(0),
|
|
.TFB(tfb_{site}),
|
|
.OQ({owire}),
|
|
.TQ({twire}),
|
|
.OFB(ofb_{site})
|
|
);""".format(**p),
|
|
file=connects)
|
|
|
|
p['ofb_connections'] = """
|
|
.OFB(ofb_{site}),
|
|
""".format(**p)
|
|
else:
|
|
p['ofb_connections'] = ''
|
|
|
|
if random.randint(0, 1):
|
|
clknet = luts.get_next_output_net()
|
|
else:
|
|
clknet = random.choice((
|
|
'clk_BUFG1',
|
|
'clk_BUFG2',
|
|
))
|
|
|
|
if random.randint(0, 1):
|
|
clkbnet = luts.get_next_output_net()
|
|
else:
|
|
clkbnet = random.choice((
|
|
'clk_BUFG1',
|
|
'clk_BUFG2',
|
|
))
|
|
|
|
if random.randint(0, 1):
|
|
oclknet = luts.get_next_output_net()
|
|
else:
|
|
oclknet = random.choice((
|
|
'clk_BUFG1',
|
|
'clk_BUFG2',
|
|
))
|
|
|
|
clkdiv = random.choice(('clk_BUFG3', 'clk_BUFG4'))
|
|
|
|
p['DISABLE_CLOCKS'] = random.randint(0, 1)
|
|
if p['DISABLE_CLOCKS']:
|
|
clknet = '0'
|
|
clkbnet = '0'
|
|
oclknet = '0'
|
|
clkdiv = '0'
|
|
|
|
print(
|
|
'''
|
|
(* KEEP, DONT_TOUCH, LOC = "{ilogic_loc}" *)
|
|
ISERDESE2 #(
|
|
.SERDES_MODE({SERDES_MODE}),
|
|
.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}),
|
|
.DYN_CLKDIV_INV_EN({DYN_CLKDIV_INV_EN}),
|
|
.DYN_CLK_INV_EN({DYN_CLK_INV_EN}),
|
|
.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}),
|
|
.IS_CLKDIV_INVERTED({IS_CLKDIV_INVERTED}),
|
|
.IS_D_INVERTED({IS_D_INVERTED}),
|
|
.OFB_USED({OFB_USED}),
|
|
.NUM_CE({NUM_CE}),
|
|
.DATA_RATE({DATA_RATE}),
|
|
.DATA_WIDTH({DATA_WIDTH}),
|
|
.IOBDELAY({IOBDELAY})
|
|
) iserdese2_{site} (
|
|
{ddly_connection}
|
|
{ofb_connections}
|
|
.D({iwire}),
|
|
.CLK({clknet}),
|
|
.CLKB({clkbnet}),
|
|
.OCLK({oclknet}),
|
|
.O({onet}),
|
|
.Q1({q1net}),
|
|
.CLKDIV({clkdiv})
|
|
);'''.format(
|
|
clkdiv=clkdiv,
|
|
clknet=clknet,
|
|
clkbnet=clkbnet,
|
|
oclknet=oclknet,
|
|
onet=luts.get_next_input_net(),
|
|
q1net=luts.get_next_input_net(),
|
|
shiftout1net=luts.get_next_input_net(),
|
|
shiftout2net=luts.get_next_input_net(),
|
|
**p),
|
|
file=connects)
|
|
|
|
|
|
def use_direct_and_iddr(p, luts, connects):
|
|
p['mux_config'] = random.choice((
|
|
'direct',
|
|
'idelay',
|
|
'none',
|
|
))
|
|
|
|
p['iddr_mux_config'] = random.choice((
|
|
'direct',
|
|
'idelay',
|
|
'none',
|
|
))
|
|
|
|
if p['iddr_mux_config'] != 'none':
|
|
p['INIT_Q1'] = random.randint(0, 1)
|
|
p['INIT_Q2'] = random.randint(0, 1)
|
|
p['IS_C_INVERTED'] = random.randint(0, 1)
|
|
p['IS_D_INVERTED'] = random.randint(0, 1)
|
|
p['SRTYPE'] = verilog.quote(random.choice(('SYNC', 'ASYNC')))
|
|
p['DDR_CLK_EDGE'] = verilog.quote(
|
|
random.choice(
|
|
(
|
|
'OPPOSITE_EDGE',
|
|
'SAME_EDGE',
|
|
'SAME_EDGE_PIPELINED',
|
|
)))
|
|
|
|
print(
|
|
'''
|
|
(* KEEP, DONT_TOUCH, LOC = "{ilogic_loc}" *)
|
|
IDDR #(
|
|
.IS_D_INVERTED({IS_D_INVERTED}),
|
|
.IS_C_INVERTED({IS_C_INVERTED}),
|
|
.INIT_Q1({INIT_Q1}),
|
|
.INIT_Q2({INIT_Q2}),
|
|
.SRTYPE({SRTYPE}),
|
|
.DDR_CLK_EDGE({DDR_CLK_EDGE})
|
|
) iddr_{site} (
|
|
.C({cnet}),
|
|
.D(iddr_d_{site}),
|
|
.Q1({q1}),
|
|
.Q2({q2})
|
|
);
|
|
'''.format(
|
|
cnet=luts.get_next_output_net(),
|
|
q1=luts.get_next_input_net(),
|
|
q2=luts.get_next_input_net(),
|
|
**p),
|
|
file=connects)
|
|
|
|
if p['iddr_mux_config'] == 'idelay' or p['mux_config'] == 'idelay' or p[
|
|
'iddr_mux_config'] == 'tristate_feedback':
|
|
print(
|
|
"""
|
|
wire idelay_{site};
|
|
|
|
(* KEEP, DONT_TOUCH, LOC = "{idelay_loc}" *)
|
|
IDELAYE2 #(
|
|
) idelay_site_{site} (
|
|
.IDATAIN({iwire}),
|
|
.DATAOUT(idelay_{site})
|
|
);""".format(**p),
|
|
file=connects)
|
|
|
|
print(
|
|
"""
|
|
assign {owire} = {onet};
|
|
assign {twire} = {tnet};
|
|
""".format(
|
|
onet=luts.get_next_output_net(),
|
|
tnet=luts.get_next_output_net(),
|
|
**p),
|
|
file=connects)
|
|
|
|
if p['iddr_mux_config'] == 'direct':
|
|
print(
|
|
'''
|
|
assign iddr_d_{site} = {iwire};'''.format(**p, ),
|
|
file=connects)
|
|
elif p['iddr_mux_config'] == 'idelay':
|
|
print(
|
|
'''
|
|
assign iddr_d_{site} = idelay_{site};'''.format(**p, ),
|
|
file=connects)
|
|
elif p['iddr_mux_config'] == 'tristate_feedback':
|
|
print(
|
|
'''
|
|
assign iddr_d_{site} = tfb_{site} ? ofb_{site} : idelay_{site};'''.format(
|
|
**p, ),
|
|
file=connects)
|
|
elif p['iddr_mux_config'] == 'none':
|
|
pass
|
|
else:
|
|
assert False, p['mux_config']
|
|
|
|
if p['mux_config'] == 'direct':
|
|
print(
|
|
'''
|
|
assign {net} = {iwire};'''.format(
|
|
net=luts.get_next_input_net(),
|
|
**p,
|
|
),
|
|
file=connects)
|
|
elif p['mux_config'] == 'idelay':
|
|
print(
|
|
'''
|
|
assign {net} = idelay_{site};'''.format(
|
|
net=luts.get_next_input_net(),
|
|
**p,
|
|
),
|
|
file=connects)
|
|
elif p['mux_config'] == 'none':
|
|
pass
|
|
else:
|
|
assert False, p['mux_config']
|
|
|
|
|
|
def run():
|
|
iostandards = [
|
|
'LVCMOS12', 'LVCMOS15', 'LVCMOS18', 'LVCMOS25', 'LVCMOS33', 'LVTTL'
|
|
]
|
|
iostandard = random.choice(iostandards)
|
|
|
|
if iostandard in ['LVTTL', 'LVCMOS18']:
|
|
drives = [4, 8, 12, 16, 24]
|
|
elif iostandard == 'LVCMOS12':
|
|
drives = [4, 8, 12]
|
|
else:
|
|
drives = [4, 8, 12, 16]
|
|
|
|
slews = ['FAST', 'SLOW']
|
|
pulls = ["NONE", "KEEPER", "PULLDOWN", "PULLUP"]
|
|
|
|
luts = lut_maker.LutMaker()
|
|
|
|
connects = io.StringIO()
|
|
|
|
tile_params = []
|
|
params = []
|
|
for idx, (tile, site) in enumerate(gen_sites()):
|
|
if idx == 0:
|
|
continue
|
|
|
|
p = {}
|
|
p['tile'] = tile
|
|
p['site'] = site
|
|
p['ilogic_loc'] = site.replace('IOB', 'ILOGIC')
|
|
p['ologic_loc'] = site.replace('IOB', 'OLOGIC')
|
|
p['idelay_loc'] = site.replace('IOB', 'IDELAY')
|
|
p['IOSTANDARD'] = verilog.quote(iostandard)
|
|
p['PULLTYPE'] = verilog.quote(random.choice(pulls))
|
|
p['DRIVE'] = random.choice(drives)
|
|
p['SLEW'] = verilog.quote(random.choice(slews))
|
|
|
|
p['pad_wire'] = 'dio[{}]'.format(idx - 1)
|
|
p['owire'] = 'do_buf[{}]'.format(idx - 1)
|
|
p['iwire'] = 'di_buf[{}]'.format(idx - 1)
|
|
p['twire'] = 't[{}]'.format(idx - 1)
|
|
|
|
params.append(p)
|
|
tile_params.append(
|
|
(
|
|
tile, site, p['pad_wire'], iostandard, p['DRIVE'],
|
|
verilog.unquote(p['SLEW']) if p['SLEW'] else None,
|
|
verilog.unquote(p['PULLTYPE'])))
|
|
|
|
write_params(tile_params)
|
|
|
|
print(
|
|
'''
|
|
`define N_DI {n_di}
|
|
|
|
module top(input clk, inout wire [`N_DI-1:0] dio);
|
|
wire [`N_DI-1:0] di_buf;
|
|
wire [`N_DI-1:0] do_buf;
|
|
wire [`N_DI-1:0] t;
|
|
|
|
wire clk_BUFG1;
|
|
wire clk_BUFG2;
|
|
wire clk_BUFG3;
|
|
wire clk_BUFG4;
|
|
|
|
(* KEEP, DONT_TOUCH *)
|
|
BUFG bufg1(
|
|
.O(clk_BUFG1)
|
|
);
|
|
(* KEEP, DONT_TOUCH *)
|
|
BUFG bufg2(
|
|
.O(clk_BUFG2)
|
|
);
|
|
(* KEEP, DONT_TOUCH *)
|
|
BUFG bufg3(
|
|
.O(clk_BUFG3)
|
|
);
|
|
(* KEEP, DONT_TOUCH *)
|
|
BUFG bufg4(
|
|
.O(clk_BUFG4)
|
|
);
|
|
'''.format(n_di=idx))
|
|
|
|
# Always output a LUT6 to make placer happy.
|
|
print(
|
|
'''
|
|
(* KEEP, DONT_TOUCH *)
|
|
LUT6 dummy_lut();
|
|
''')
|
|
|
|
any_idelay = False
|
|
|
|
for p in params:
|
|
print(
|
|
'''
|
|
wire iddr_d_{site};
|
|
|
|
(* KEEP, DONT_TOUCH, LOC = "{site}" *)
|
|
IOBUF #(
|
|
.IOSTANDARD({IOSTANDARD})
|
|
) ibuf_{site} (
|
|
.IO({pad_wire}),
|
|
.I({owire}),
|
|
.O({iwire}),
|
|
.T({twire})
|
|
);
|
|
'''.format(**p),
|
|
file=connects)
|
|
|
|
p['use_iserdese2'] = random.randint(0, 1)
|
|
if p['use_iserdese2']:
|
|
use_iserdese2(p, luts, connects)
|
|
else:
|
|
use_direct_and_iddr(p, luts, connects)
|
|
|
|
if p['iddr_mux_config'] == 'idelay' or p['mux_config'] == 'idelay':
|
|
any_idelay = True
|
|
|
|
if any_idelay:
|
|
print("""
|
|
(* KEEP, DONT_TOUCH *)
|
|
IDELAYCTRL();""")
|
|
|
|
for l in luts.create_wires_and_luts():
|
|
print(l)
|
|
|
|
print(connects.getvalue())
|
|
|
|
print("endmodule")
|
|
|
|
with open('params.jl', 'w') as f:
|
|
json.dump(params, f, indent=2)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
run()
|