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