diff --git a/fuzzers/030-iob/Makefile b/fuzzers/030-iob/Makefile index e8339150..a3533751 100644 --- a/fuzzers/030-iob/Makefile +++ b/fuzzers/030-iob/Makefile @@ -1,10 +1,15 @@ N := 50 +SPECIMENS_DEPS := build/iobanks.txt include ../fuzzer.mk database: build/segbits_xiob33.db +build/iobanks.txt: write_io_banks.tcl + mkdir -p build + cd build/ && ${XRAY_VIVADO} -mode batch -source ${FUZDIR}/write_io_banks.tcl + build/segbits_xiob33.rdb: $(SPECIMENS_OK) - ${XRAY_SEGMATCH} -c 7 -o build/segbits_xiob33.rdb $$(find -name segdata_liob33.txt) $$(find -name segdata_riob33.txt) + ${XRAY_SEGMATCH} -c 12 -o build/segbits_xiob33.rdb $$(find -name segdata_liob33.txt) $$(find -name segdata_riob33.txt) build/segbits_xiob33.db: build/segbits_xiob33.rdb process_rdb.py bits.dbf python3 process_rdb.py build/segbits_xiob33.rdb > build/segbits_xiob33_processed.rdb diff --git a/fuzzers/030-iob/bits.dbf b/fuzzers/030-iob/bits.dbf index c0eca9c9..09d2e838 100644 --- a/fuzzers/030-iob/bits.dbf +++ b/fuzzers/030-iob/bits.dbf @@ -1,4 +1,4 @@ 38_92 39_93 38_94,IOB33.IOB_Y0.PULLTYPE.PULLDOWN -38_106 38_110 39_105 39_109,IOB33.IOB_Y0.SLEW.FAST +38_106 39_107 39_111 38_106 38_110 39_105 39_109,IOB33.IOB_Y0.SLEW.FAST 39_33 38_34 39_35,IOB33.IOB_Y1.PULLTYPE.PULLDOWN -38_18 38_22 39_17 39_21,IOB33.IOB_Y1.SLEW.FAST +39_21 38_16 38_20 38_18 38_22 39_17,IOB33.IOB_Y1.SLEW.FAST diff --git a/fuzzers/030-iob/check_results.py b/fuzzers/030-iob/check_results.py index affe8942..93acb0aa 100644 --- a/fuzzers/030-iob/check_results.py +++ b/fuzzers/030-iob/check_results.py @@ -24,11 +24,11 @@ def process_parts(parts): if parts[0] == 'INOUT': yield 'type', 'IOBUF_INTERMDISABLE' - if parts[0] == 'IN_ONLY': + if parts[-1] == 'IN_ONLY': yield 'type', 'IBUF' - if parts[0] == 'SLEW': - yield 'SLEW', verilog.quote(parts[1]) + if len(parts) > 2 and parts[-2] == 'SLEW': + yield 'SLEW', verilog.quote(parts[-1]) if parts[0] == 'PULLTYPE': yield 'PULLTYPE', verilog.quote(parts[1]) @@ -38,7 +38,11 @@ def process_parts(parts): if len(parts) > 1 and parts[1] == 'DRIVE': yield 'IOSTANDARDS', parts[0].split('_') - yield 'DRIVES', parts[2].split('_') + + if parts[2] == 'I_FIXED': + yield 'DRIVES', [None] + else: + yield 'DRIVES', parts[2].split('_') def create_sites_from_fasm(root): @@ -67,7 +71,7 @@ def create_sites_from_fasm(root): sites[key]['type'] = None else: assert 'IOSTANDARDS' in sites[key], sites[key] - assert 'DRIVES' in sites[key] + assert 'DRIVES' in sites[key], sites[key] sites[key]['type'] = "OBUF" return sites @@ -76,10 +80,10 @@ def create_sites_from_fasm(root): def process_specimen(root): sites = create_sites_from_fasm(root) - with open(os.path.join(root, 'params.jl')) as f: + with open(os.path.join(root, 'params.json')) as f: params = json.load(f) - for p in params: + for p in params['tiles']: tile = p['tile'] site = p['site'] site_y = int(site[site.find('Y') + 1:]) % 2 @@ -96,7 +100,7 @@ def process_specimen(root): site_from_fasm = sites[(tile, site_key)] assert p['type'] == site_from_fasm['type'], ( - tile, site_key, p, site_from_fasm) + tile, site_key, p['type'], site_from_fasm['type']) if p['type'] is None: continue @@ -104,16 +108,28 @@ def process_specimen(root): assert p['PULLTYPE'] == site_from_fasm['PULLTYPE'], ( tile, site_key, p, site_from_fasm) + assert 'IOSTANDARDS' in site_from_fasm, (root, tile, site) + assert verilog.unquote( p['IOSTANDARD']) in site_from_fasm['IOSTANDARDS'], ( - tile, site_key, p, site_from_fasm) + p['IOSTANDARD'], + site_from_fasm['IOSTANDARDS'], + ) if p['type'] != 'IBUF': assert p['SLEW'] == site_from_fasm['SLEW'], ( tile, site_key, p, site_from_fasm) - assert 'I{}'.format(p['DRIVE']) in site_from_fasm['DRIVES'], ( - tile, site_key, p, site_from_fasm) + assert 'DRIVES' not in p, p + assert 'DRIVES' in site_from_fasm, ( + tile, site, p['type'], site_from_fasm) + + if p['DRIVE'] is None: + assert None in site_from_fasm['DRIVES'], ( + tile, site_key, p['DRIVE'], site_from_fasm['DRIVES']) + else: + assert 'I{}'.format(p['DRIVE']) in site_from_fasm['DRIVES'], ( + tile, site_key, p['DRIVE'], site_from_fasm['DRIVES']) def main(): diff --git a/fuzzers/030-iob/generate.py b/fuzzers/030-iob/generate.py index a110d10a..97fb3a04 100644 --- a/fuzzers/030-iob/generate.py +++ b/fuzzers/030-iob/generate.py @@ -15,6 +15,8 @@ def bitfilter(frame, word): def mk_drive_opt(iostandard, drive): + if drive is None: + drive = '_FIXED' return '{}.DRIVE.I{}'.format(iostandard, drive) @@ -37,13 +39,15 @@ def drives_for_iostandard(iostandard): drives = [4, 8, 12, 16, 24] elif iostandard == 'LVCMOS12': drives = [4, 8, 12] + elif iostandard == 'SSTL135': + return ['_FIXED'] else: drives = [4, 8, 12, 16] return drives -STEPDOWN_IOSTANDARDS = ['LVCMOS12', 'LVCMOS15', 'LVCMOS18'] +STEPDOWN_IOSTANDARDS = ['LVCMOS12', 'LVCMOS15', 'LVCMOS18', 'SSTL135'] def main(): @@ -54,10 +58,10 @@ def main(): di[0],IOB_X0Y107,LIOB33_X0Y107,A21,PULLDOWN di[10],IOB_X0Y147,LIOB33_X0Y147,F14,PULLUP ''' - with open('params.jl', 'r') as f: + with open('params.json', 'r') as f: design = json.load(f) - for d in design: + for d in design['tiles']: site = d['site'] if skip_broken_tiles(d): @@ -65,44 +69,31 @@ def main(): iostandard = verilog.unquote(d['IOSTANDARD']) - stepdown = iostandard in STEPDOWN_IOSTANDARDS - segmk.add_site_tag(site, '_'.join(STEPDOWN_IOSTANDARDS), stepdown) + segmk.add_site_tag( + site, '_'.join(STEPDOWN_IOSTANDARDS) + '.STEPDOWN', + iostandard in STEPDOWN_IOSTANDARDS) if d['type'] is None: segmk.add_site_tag(site, 'INOUT', 0) segmk.add_site_tag(site, '{}.IN_USE'.format(iostandard), 0) segmk.add_site_tag(site, '{}.IN'.format(iostandard), 0) segmk.add_site_tag(site, '{}.OUT'.format(iostandard), 0) - for drive in drives_for_iostandard(iostandard): - segmk.add_site_tag( - site, '{}.DRIVE.I{}.IN_OUT_COMMON'.format( - iostandard, drive), 0) + segmk.add_site_tag(site, '{}.IN_ONLY'.format(iostandard), 0) elif d['type'] == 'IBUF': segmk.add_site_tag(site, 'INOUT', 0) segmk.add_site_tag(site, '{}.IN_USE'.format(iostandard), 1) segmk.add_site_tag(site, '{}.IN'.format(iostandard), 1) segmk.add_site_tag(site, '{}.OUT'.format(iostandard), 0) - for drive in drives_for_iostandard(iostandard): - segmk.add_site_tag( - site, '{}.DRIVE.I{}.IN_OUT_COMMON'.format( - iostandard, drive), 1) + segmk.add_site_tag(site, '{}.IN_ONLY'.format(iostandard), 1) elif d['type'] == 'OBUF': segmk.add_site_tag(site, 'INOUT', 0) segmk.add_site_tag(site, '{}.IN_USE'.format(iostandard), 1) segmk.add_site_tag(site, '{}.IN'.format(iostandard), 0) segmk.add_site_tag(site, '{}.OUT'.format(iostandard), 1) - for drive in drives_for_iostandard(iostandard): - if drive == d['DRIVE']: - segmk.add_site_tag( - site, '{}.DRIVE.I{}.IN_OUT_COMMON'.format( - iostandard, drive), 1) - else: - segmk.add_site_tag( - site, '{}.DRIVE.I{}.IN_OUT_COMMON'.format( - iostandard, drive), 0) elif d['type'] == 'IOBUF_INTERMDISABLE': segmk.add_site_tag(site, 'INOUT', 1) segmk.add_site_tag(site, '{}.IN_USE'.format(iostandard), 1) + segmk.add_site_tag(site, '{}.IN'.format(iostandard), 1) segmk.add_site_tag(site, '{}.OUT'.format(iostandard), 1) if d['type'] is not None: @@ -126,13 +117,16 @@ def main(): drive_opts.add(mk_drive_opt(opt, drive_opt)) + drive_opts.add(mk_drive_opt("SSTL135", None)) + segmaker.add_site_group_zero( segmk, site, '', drive_opts, mk_drive_opt('LVCMOS25', '12'), mk_drive_opt(iostandard, d['DRIVE'])) - segmaker.add_site_group_zero( - segmk, site, "SLEW.", ("SLOW", "FAST"), "FAST", - verilog.unquote(d['SLEW'])) + for opt in ["SLOW", "FAST"]: + segmk.add_site_tag( + site, iostandard + ".SLEW." + opt, opt == verilog.unquote( + d['SLEW'])) if 'ibufdisable_wire' in d: segmk.add_site_tag( diff --git a/fuzzers/030-iob/generate.tcl b/fuzzers/030-iob/generate.tcl index 8cae847b..e0813c0e 100644 --- a/fuzzers/030-iob/generate.tcl +++ b/fuzzers/030-iob/generate.tcl @@ -75,12 +75,25 @@ proc loc_pins {} { } } +proc set_vref {} { + set fp [open "iobank_vref.csv" r] + for {gets $fp line} {$line != ""} {gets $fp line} { + set parts [split $line ","] + set iobank [lindex $parts 0] + set vref [lindex $parts 1] + puts "setting $iobank ([get_iobanks $iobank]) to INTERNAL_VREF $vref" + set_property INTERNAL_VREF $vref [get_iobanks $iobank] + } +} + proc run {} { create_project -force -part $::env(XRAY_PART) design design read_verilog top.v synth_design -top top loc_pins + set_vref + set_property CFGBVS VCCO [current_design] set_property CONFIG_VOLTAGE 3.3 [current_design] diff --git a/fuzzers/030-iob/process_rdb.py b/fuzzers/030-iob/process_rdb.py index abdd7ecc..004683f8 100644 --- a/fuzzers/030-iob/process_rdb.py +++ b/fuzzers/030-iob/process_rdb.py @@ -66,127 +66,129 @@ def main(): iostandard_lines = [] with open(args.input_rdb) as f: for l in f: - if ('.LVCMOS' in l or '.LVTTL' in l) and 'IOB_' in l: + if ('.SSTL135' in l or '.LVCMOS' in l + or '.LVTTL' in l) and 'IOB_' in l: iostandard_lines.append(l) else: print(l.strip()) - common_in_only_bits = { - 'IOB_Y0': set(), - 'IOB_Y1': set(), - } - for l in iostandard_lines: - if 'IN_OUT_COMMON' in l: - common_in_only_bits[get_site(l)] |= parse_bits(l) - - for site in sorted(common_in_only_bits): - print( - 'IOB33.{}.IN_ONLY'.format(site), ' '.join( - common_in_only_bits[site])) - - iostandard_in = {} - outs = {} - drives = {} - in_use = {} + sites = {} for l in iostandard_lines: - name = get_name(l) + feature = get_name(l) + feature_parts = feature.split('.') site = get_site(l) - iostandard = name.split('.')[2] + iostandard = feature_parts[2] - if name.endswith('.IN_USE'): - in_use[(site, iostandard)] = parse_bits(l) + bits = parse_bits(l) + bits = filter_bits(site, bits) - for l in iostandard_lines: - name = get_name(l) - site = get_site(l) - iostandard = name.split('.')[2] + if site not in sites: + sites[site] = {} - if name.endswith('.IN'): - in_bits = parse_bits(l) | in_use[(site, iostandard)] + group = feature_parts[3] + if group not in sites[site]: + sites[site][group] = {} - if in_bits not in iostandard_in: - iostandard_in[in_bits] = [] + if group in ['DRIVE', 'SLEW']: + enum = feature_parts[4] + sites[site][group][(iostandard, enum)] = bits + elif group in ['IN', 'IN_ONLY', 'IN_USE', 'OUT', 'STEPDOWN']: + sites[site][group][(iostandard, None)] = bits + else: + assert False, group - iostandard_in[in_bits].append((site, iostandard)) + for site in sites: + for iostandard, enum in sites[site]['DRIVE']: + sites[site]['DRIVE'][(iostandard, enum)] |= sites[site]['OUT'][( + iostandard, None)] - if name.endswith('.OUT'): - outs[(site, - iostandard)] = parse_bits(l) | in_use[(site, iostandard)] - - if '.DRIVE.' in name and '.IN_OUT_COMMON' not in name: - drive = name.split('.')[-1] - if (site, iostandard) not in drives: - drives[(site, iostandard)] = {} - - if drive not in drives[(site, iostandard)]: - drives[(site, iostandard)][drive] = {} - - drives[(site, iostandard)][drive] = filter_bits( - site, parse_bits(l)) - - common_in_bits = { - 'IOB_Y0': set(), - 'IOB_Y1': set(), - } - - for bits in sorted(iostandard_in.keys()): - sites, standards = zip(*iostandard_in[bits]) - - site = set(sites) - - assert len(site) == 1, site - site = site.pop() - - common_in_bits[site] |= bits - - for bits in sorted(iostandard_in.keys()): - sites, standards = zip(*iostandard_in[bits]) - - site = set(sites) - - assert len(site) == 1, site - site = site.pop() - - neg_bits = set('!' + bit for bit in (common_in_bits[site] - bits)) - - print( - 'IOB33.{}.{}.IN'.format(site, '_'.join(standards)), - ' '.join(bits | neg_bits)) - - iodrives = {} + for iostandard, enum in sites[site]['IN']: + sites[site]['IN_ONLY'][(iostandard, enum)] -= sites[site]['IN'][( + iostandard, enum)] common_bits = {} + for site in sites: + for group in sites[site]: + if (site, group) not in common_bits: + common_bits[(site, group)] = set() - for site, iostandard in drives: - for drive in drives[(site, iostandard)]: - combined_bits = drives[(site, iostandard)][drive] | outs[( - site, iostandard)] + for bits in sites[site][group].values(): + common_bits[(site, group)] |= bits - if site not in common_bits: - common_bits[site] = set(common_in_only_bits[site]) + slew_in_drives = {} - common_bits[site] |= combined_bits + for site in sites: + common_bits[(site, 'DRIVE')] -= common_bits[(site, 'SLEW')] + common_bits[(site, 'DRIVE')] -= common_bits[(site, 'STEPDOWN')] + common_bits[(site, 'IN_ONLY')] |= common_bits[(site, 'DRIVE')] + common_bits[(site, 'IN_ONLY')] -= common_bits[(site, 'STEPDOWN')] - if combined_bits not in iodrives: - iodrives[combined_bits] = [] + for iostandard, enum in sites[site]['DRIVE']: + slew_in_drive = common_bits[ + (site, 'SLEW')] & sites[site]['DRIVE'][(iostandard, enum)] + if slew_in_drive: + if (site, iostandard) not in slew_in_drives: + slew_in_drives[(site, iostandard)] = set() - iodrives[combined_bits].append((site, iostandard, drive)) + slew_in_drives[(site, iostandard)] |= slew_in_drive + sites[site]['DRIVE'][(iostandard, enum)] -= slew_in_drive - for bits in iodrives: - sites, standards, drives = zip(*iodrives[bits]) + sites[site]['DRIVE'][(iostandard, + enum)] -= common_bits[(site, 'STEPDOWN')] - site = set(sites) + for site, iostandard in slew_in_drives: + for _, enum in sites[site]['SLEW']: + sites[site]['SLEW'][(iostandard, + enum)] |= slew_in_drives[(site, iostandard)] - assert len(site) == 1, site - site = site.pop() + for site in sites: + del sites[site]['OUT'] + del sites[site]['IN_USE'] - neg_bits = set('!' + bit for bit in (common_bits[site] - bits)) + for site in sites: + for group in sites[site]: + common_groups = {} - print( - 'IOB33.{}.{}.DRIVE.{}'.format( - site, '_'.join(sorted(set(standards))), '_'.join( - sorted(set(drives)))), ' '.join(bits | neg_bits)) + # Merge features that are identical. + # + # For example: + # + # IOB33.IOB_Y1.LVCMOS15.IN 38_42 39_41 + # IOB33.IOB_Y1.LVCMOS18.IN 38_42 39_41 + # + # Must be grouped. + for (iostandard, enum), bits in sites[site][group].items(): + if bits not in common_groups: + common_groups[bits] = { + 'IOSTANDARDS': set(), + 'enums': set(), + } + + common_groups[bits]['IOSTANDARDS'].add(iostandard) + if enum is not None: + common_groups[bits]['enums'].add(enum) + + for bits, v in common_groups.items(): + if v['enums']: + feature = 'IOB33.{site}.{iostandards}.{group}.{enums}'.format( + site=site, + iostandards='_'.join(sorted(v['IOSTANDARDS'])), + group=group, + enums='_'.join(sorted(v['enums'])), + ) + else: + feature = 'IOB33.{site}.{iostandards}.{group}'.format( + site=site, + iostandards='_'.join(sorted(v['IOSTANDARDS'])), + group=group, + ) + + neg_bits = frozenset( + '!{}'.format(b) + for b in (common_bits[(site, group)] - bits)) + print( + '{} {}'.format(feature, ' '.join(sorted(bits | neg_bits)))) if __name__ == "__main__": diff --git a/fuzzers/030-iob/top.py b/fuzzers/030-iob/top.py index 3a0700b1..35b2222d 100644 --- a/fuzzers/030-iob/top.py +++ b/fuzzers/030-iob/top.py @@ -45,14 +45,22 @@ def run(): io_idx = 0 iostandards = [ - 'LVCMOS12', 'LVCMOS15', 'LVCMOS18', 'LVCMOS25', 'LVCMOS33', 'LVTTL' + 'LVCMOS12', + 'LVCMOS15', + 'LVCMOS18', + 'LVCMOS25', + 'LVCMOS33', + 'LVTTL', + 'SSTL135', ] iostandard = random.choice(iostandards) if iostandard in ['LVTTL', 'LVCMOS18']: drives = [4, 8, 12, 16, 24] - elif iostandard == 'LVCMOS12': + elif iostandard in ['LVCMOS12']: drives = [4, 8, 12] + elif iostandard == 'SSTL135': + drives = None else: drives = [4, 8, 12, 16] @@ -64,7 +72,26 @@ def run(): connects = io.StringIO() tile_params = [] - params = [] + params = { + "tiles": [], + 'INTERNAL_VREF': {}, + } + + with open(os.path.join(os.getenv('FUZDIR'), 'build', 'iobanks.txt')) as f: + iobanks = [int(l.strip()) for l in f] + + params['iobanks'] = iobanks + + if iostandard in ['SSTL135']: + for iobank in iobanks: + params['INTERNAL_VREF'][iobank] = random.choice( + ( + .600, + .675, + .75, + .90, + )) + any_idelay = False for tile, site in gen_sites(): p = {} @@ -93,7 +120,10 @@ def run(): elif p['type'] == 'OBUF': p['pad_wire'] = 'do[{}]'.format(o_idx) p['iwire'] = luts.get_next_output_net() - p['DRIVE'] = random.choice(drives) + if drives is not None: + p['DRIVE'] = random.choice(drives) + else: + p['DRIVE'] = None p['SLEW'] = verilog.quote(random.choice(slews)) o_idx += 1 @@ -101,7 +131,10 @@ def run(): p['pad_wire'] = 'dio[{}]'.format(io_idx) p['iwire'] = luts.get_next_output_net() p['owire'] = luts.get_next_input_net() - p['DRIVE'] = random.choice(drives) + if drives is not None: + p['DRIVE'] = random.choice(drives) + else: + p['DRIVE'] = None p['SLEW'] = verilog.quote(random.choice(slews)) p['tristate_wire'] = random.choice( ('0', luts.get_next_output_net())) @@ -111,7 +144,11 @@ def run(): ('0', luts.get_next_output_net())) io_idx += 1 - params.append(p) + if 'DRIVE' in p: + if p['DRIVE'] is not None: + p['DRIVE_STR'] = '.DRIVE({}),'.format(p['DRIVE']) + else: + p['DRIVE_STR'] = '' if p['type'] is not None: tile_params.append( @@ -119,9 +156,14 @@ def run(): tile, site, p['pad_wire'], iostandard, p['DRIVE'], verilog.unquote(p['SLEW']) if p['SLEW'] else None, verilog.unquote(p['PULLTYPE']))) + params['tiles'].append(p) write_params(tile_params) + with open('iobank_vref.csv', 'w') as f: + for iobank, vref in params['INTERNAL_VREF'].items(): + f.write('{},{}\n'.format(iobank, vref)) + print( ''' `define N_DI {n_di} @@ -141,7 +183,7 @@ module top(input wire [`N_DI-1:0] di, output wire [`N_DO-1:0] do, inout wire [`N (* KEEP, DONT_TOUCH *) LUT6 dummy_lut();''') - for p in params: + for p in params['tiles']: if p['type'] is None: continue elif p['type'] == 'IBUF': @@ -173,7 +215,7 @@ module top(input wire [`N_DI-1:0] di, output wire [`N_DO-1:0] do, inout wire [`N (* KEEP, DONT_TOUCH *) OBUF #( .IOSTANDARD({IOSTANDARD}), - .DRIVE({DRIVE}), + {DRIVE_STR} .SLEW({SLEW}) ) ibuf_{site} ( .O({pad_wire}), @@ -186,7 +228,7 @@ module top(input wire [`N_DI-1:0] di, output wire [`N_DO-1:0] do, inout wire [`N (* KEEP, DONT_TOUCH *) IOBUF_INTERMDISABLE #( .IOSTANDARD({IOSTANDARD}), - .DRIVE({DRIVE}), + {DRIVE_STR} .SLEW({SLEW}) ) ibuf_{site} ( .IO({pad_wire}), @@ -205,7 +247,7 @@ module top(input wire [`N_DI-1:0] di, output wire [`N_DO-1:0] do, inout wire [`N print("endmodule") - with open('params.jl', 'w') as f: + with open('params.json', 'w') as f: json.dump(params, f, indent=2)