#!/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()