From 90d420eef377625a41615337720653b8fd2c4a02 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Fri, 2 Oct 2020 14:44:41 -0700 Subject: [PATCH] Add initial MMCM feature and PIP support. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fuzzers/000-init-db/Makefile | 2 + fuzzers/005-tilegrid/Makefile | 5 +- fuzzers/005-tilegrid/add_tdb.py | 2 +- fuzzers/031-cmt-mmcm/Makefile | 29 +- fuzzers/031-cmt-mmcm/README.md | 5 +- fuzzers/031-cmt-mmcm/generate.py | 204 ++++++--- fuzzers/031-cmt-mmcm/generate.tcl | 103 ++++- fuzzers/031-cmt-mmcm/top.py | 424 ++++++++++++------ fuzzers/031-cmt-mmcm/write_mmcm_reg.py | 316 +++++++++++++ fuzzers/034b-cmt-mmcm-pips/Makefile | 55 +++ fuzzers/034b-cmt-mmcm-pips/README.md | 20 + .../034b-cmt-mmcm-pips/cmt_top_lower_b.tcl | 46 ++ fuzzers/034b-cmt-mmcm-pips/fixup_and_group.py | 255 +++++++++++ fuzzers/034b-cmt-mmcm-pips/generate.py | 221 +++++++++ fuzzers/034b-cmt-mmcm-pips/generate.tcl | 192 ++++++++ fuzzers/034b-cmt-mmcm-pips/tag_groups.txt | 0 fuzzers/034b-cmt-mmcm-pips/top.py | 419 +++++++++++++++++ fuzzers/Makefile | 2 + fuzzers/int_maketodo.py | 2 +- prjxray/segmaker.py | 2 + utils/mergedb.sh | 6 + 21 files changed, 2080 insertions(+), 230 deletions(-) create mode 100644 fuzzers/031-cmt-mmcm/write_mmcm_reg.py create mode 100644 fuzzers/034b-cmt-mmcm-pips/Makefile create mode 100644 fuzzers/034b-cmt-mmcm-pips/README.md create mode 100644 fuzzers/034b-cmt-mmcm-pips/cmt_top_lower_b.tcl create mode 100755 fuzzers/034b-cmt-mmcm-pips/fixup_and_group.py create mode 100644 fuzzers/034b-cmt-mmcm-pips/generate.py create mode 100644 fuzzers/034b-cmt-mmcm-pips/generate.tcl create mode 100644 fuzzers/034b-cmt-mmcm-pips/tag_groups.txt create mode 100644 fuzzers/034b-cmt-mmcm-pips/top.py diff --git a/fuzzers/000-init-db/Makefile b/fuzzers/000-init-db/Makefile index cf7e0325..2cb3a41a 100644 --- a/fuzzers/000-init-db/Makefile +++ b/fuzzers/000-init-db/Makefile @@ -46,6 +46,8 @@ DB_SIMPLE=\ $(addsuffix _r, $(DB_SIMPLE_LR) $(DB_SIMPLE_R)) \ segbits_cmt_top_l_upper_t \ segbits_cmt_top_r_upper_t \ + segbits_cmt_top_l_lower_b \ + segbits_cmt_top_r_lower_b \ segbits_rioi3 \ segbits_riob33 \ segbits_hclk_cmt \ diff --git a/fuzzers/005-tilegrid/Makefile b/fuzzers/005-tilegrid/Makefile index e568eeb6..35eb3b15 100644 --- a/fuzzers/005-tilegrid/Makefile +++ b/fuzzers/005-tilegrid/Makefile @@ -33,11 +33,10 @@ GENERATE_FULL_ARGS= ifeq (${XRAY_DATABASE}, zynq7) # Zynq7 only TILEGRID_TDB_DEPENDENCIES += ps7_int/$(BUILD_FOLDER)/segbits_tilegrid.tdb -else -# Artix7/Kintex7 only -TILEGRID_TDB_DEPENDENCIES += mmcm/$(BUILD_FOLDER)/segbits_tilegrid.tdb endif +TILEGRID_TDB_DEPENDENCIES += mmcm/$(BUILD_FOLDER)/segbits_tilegrid.tdb + ifeq (${XRAY_DATABASE}, kintex7) TILEGRID_TDB_DEPENDENCIES += orphan_int_column/$(BUILD_FOLDER)/segbits_tilegrid.tdb else diff --git a/fuzzers/005-tilegrid/add_tdb.py b/fuzzers/005-tilegrid/add_tdb.py index 3dcc9d13..8f1873f5 100644 --- a/fuzzers/005-tilegrid/add_tdb.py +++ b/fuzzers/005-tilegrid/add_tdb.py @@ -89,7 +89,7 @@ def run(fn_in, fn_out, verbose=False): tdb_fns = [ ("iob", 42, 4), ("ioi", 42, 4), - ("mmcm", 30, 101), + ("mmcm", 30, 49), ("pll", 30, 27), ("monitor", 30, 101), ("bram", 28, 10), diff --git a/fuzzers/031-cmt-mmcm/Makefile b/fuzzers/031-cmt-mmcm/Makefile index c3c3ded6..c4e7377c 100644 --- a/fuzzers/031-cmt-mmcm/Makefile +++ b/fuzzers/031-cmt-mmcm/Makefile @@ -5,25 +5,26 @@ # https://opensource.org/licenses/ISC # # SPDX-License-Identifier: ISC -# read/write width is relatively slow to resolve -# Even slower with multi bit masks... -N := 14 +N := 50 include ../fuzzer.mk -SEGDATAS=$(addsuffix /segdata_cmt_top_r_lower_b.txt,$(SPECIMENS)) +database: build/segbits_cmt_top_lower_b.db -database: build/segbits_cmt_top_r_lower_b.db +build/segbits_cmt_top_lower_b.rdb: $(SPECIMENS_OK) + ${XRAY_SEGMATCH} -c 150 -o build/segbits_cmt_top_lower_b.rdb \ + $(shell find build -name "segdata_cmt_top_*_lower_b.txt") -build/segbits_cmt_top_r_lower_b.rdb: $(SPECIMENS_OK) - ${XRAY_SEGMATCH} -o build/segbits_cmt_top_r_lower_b.rdb $(SEGDATAS) +build/segbits_cmt_top_lower_b.db: build/segbits_cmt_top_lower_b.rdb write_mmcm_reg.py + python3 write_mmcm_reg.py \ + --seg_in build/segbits_cmt_top_lower_b.rdb \ + > build/segbits_cmt_top_lower_b.rdb2 + ${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf \ + --seg-fn-in build/segbits_cmt_top_lower_b.rdb2 \ + --seg-fn-out build/segbits_cmt_top_lower_b.db -build/segbits_cmt_top_r_lower_b.db: build/segbits_cmt_top_r_lower_b.rdb - ${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf --seg-fn-in $^ --seg-fn-out $@ - ${XRAY_MASKMERGE} build/mask_cmt_top_r_lower_b.db $(SEGDATAS) - -pushdb: - ${XRAY_MERGEDB} cmt_top_r_lower_b build/segbits_cmt_top_r_lower_b.db - ${XRAY_MERGEDB} mask_cmt_top_r_lower_b build/mask_cmt_top_r_lower_b.db +pushdb: database + ${XRAY_MERGEDB} cmt_top_r_lower_b build/segbits_cmt_top_lower_b.db + ${XRAY_MERGEDB} cmt_top_l_lower_b build/segbits_cmt_top_lower_b.db .PHONY: database pushdb diff --git a/fuzzers/031-cmt-mmcm/README.md b/fuzzers/031-cmt-mmcm/README.md index eec2c99e..455715a0 100644 --- a/fuzzers/031-cmt-mmcm/README.md +++ b/fuzzers/031-cmt-mmcm/README.md @@ -1,3 +1,4 @@ -# MMCM +# Clock Management Tile (CMT) - MMCM Fuzzer -`MMCME2_ADV` in [UG953](https://www.xilinx.com/support/documentation/sw_manuals/xilinx2017_2/ug953-vivado-7series-libraries.pdf) lists the available attributes. +Bits that are part of the dynamic configration register interface (see APPNOTE +XAPP888) are handled specially. diff --git a/fuzzers/031-cmt-mmcm/generate.py b/fuzzers/031-cmt-mmcm/generate.py index fa4edee6..37841781 100644 --- a/fuzzers/031-cmt-mmcm/generate.py +++ b/fuzzers/031-cmt-mmcm/generate.py @@ -15,57 +15,158 @@ from prjxray.segmaker import Segmaker from prjxray import verilog -def clkout_tags(segmk, ps, site): - """ - Special bit for value 1 (bypass), all bits off for value 128. - Two 7 bit counters, sharing LSB (one counter is value+1, inverting the LSB). - """ - for param, tagname in [('CLKOUT1_DIVIDE', 'ZCLKOUT1_DIVIDE')]: - value = int(ps[param]) +def bitfilter(frame, word): + if frame < 28: + return False - # bypass bit - segmk.add_site_tag(site, '%s_NODIV' % param, value == 1) - - bitstr = [int(x) for x in "{0:08b}".format(value)[::-1]] - bitstr2 = [int(x) for x in "{0:08b}".format(value + 1)[::-1]] - for i in range(7): - mybit = bitstr[i] - mybit2 = bitstr2[i] - if i == 0: - # shared (inverted) LSB - mybit2 = 1 ^ bitstr2[i] - assert mybit == mybit2, "{} value {} has invalid bit0 at".format( - param, value) - - # special cases - if value == 1: - if i == 0: - mybit = 0 - mybit2 = 0 - elif i == 1: - mybit = 1 - elif value == 128: - mybit = 0 - mybit2 = 0 - - segmk.add_site_tag(site, '%s_CNT0_[%u]' % (param, i), mybit) - segmk.add_site_tag(site, '%s_CNT1_[%u]' % (param, i), mybit2) + return True -def misc_tags(segmk, ps, site): - for boolattr in [ - 'STARTUP_WAIT', - "CLKOUT4_CASCADE", - "CLKFBOUT_USE_FINE_PS", - "CLKOUT0_USE_FINE_PS", - "CLKOUT1_USE_FINE_PS", - "CLKOUT2_USE_FINE_PS", - "CLKOUT3_USE_FINE_PS", - #"CLKOUT4_USE_FINE_PS", # several bits are changing, needs investigation - "CLKOUT5_USE_FINE_PS", - "CLKOUT6_USE_FINE_PS" +def bus_tags(segmk, ps, site): + segmk.add_site_tag(site, 'IN_USE', ps['active']) + + if not ps['active']: + return + + for k in ps: + segmk.add_site_tag(site, 'param_' + k + '_' + str(ps[k]), 1) + + for reg, invert in [ + ('RST', 1), + ('PWRDWN', 1), + ('CLKINSEL', 0), ]: - segmk.add_site_tag(site, boolattr, ps[boolattr] == '"TRUE"') + opt = 'IS_{}_INVERTED'.format(reg) + + if invert: + segmk.add_site_tag(site, 'ZINV_' + reg, 1 ^ ps[opt]) + else: + segmk.add_site_tag(site, 'INV_' + reg, ps[opt]) + + for opt in ['OPTIMIZED', 'HIGH', 'LOW']: + if verilog.unquote(ps['BANDWIDTH']) == opt: + segmk.add_site_tag(site, 'BANDWIDTH.' + opt, 1) + elif verilog.unquote(ps['BANDWIDTH']) == 'LOW': + segmk.add_site_tag(site, 'BANDWIDTH.' + opt, 0) + + for opt in ['ZHOLD', 'BUF_IN', 'EXTERNAL', 'INTERNAL']: + continue + + opt_match = verilog.unquote(ps['COMPENSATION']) == opt + + if ps['clkfbin_conn'] == '': + segmk.add_site_tag(site, 'COMP.NOFB_' + opt, opt_match) + segmk.add_site_tag(site, 'COMP.ZNOFB_' + opt, opt_match) + continue + + for conn in ['clk', 'clkfbout_mult_BUFG_' + ps['site'], + 'clkfbout_mult_' + ps['site']]: + conn_match = ps['clkfbin_conn'] == conn + segmk.add_site_tag( + site, 'COMP.' + opt + '_' + conn + '_' + ps['site'], opt_match + and conn_match) + segmk.add_site_tag( + site, 'COMP.Z' + opt + '_' + conn + '_' + ps['site'], + not opt_match and conn_match) + segmk.add_site_tag( + site, 'COMP.Z' + opt + '_Z' + conn + '_' + ps['site'], + not opt_match and not conn_match) + segmk.add_site_tag( + site, 'COMP.' + opt + '_Z' + conn + '_' + ps['site'], opt_match + and not conn_match) + + #bufg_on_clkin = \ + # 'BUFG' in ps['clkin1_conn'] or \ + # 'BUFG' in ps['clkin2_conn'] + + # This one is in conflict with some clock routing bits. + # match = verilog.unquote(ps['COMPENSATION']) in ['BUF_IN', 'EXTERNAL'] + # if not match: + # if verilog.unquote(ps['COMPENSATION']) == 'ZHOLD' and bufg_on_clkin: + # match = True + # segmk.add_site_tag( + # site, 'COMPENSATION.BUF_IN_OR_EXTERNAL_OR_ZHOLD_CLKIN_BUF', match) + + #match = verilog.unquote(ps['COMPENSATION']) in ['ZHOLD'] + #segmk.add_site_tag( + # site, 'COMPENSATION.Z_ZHOLD_OR_CLKIN_BUF', not match + # or (match and bufg_on_clkin)) + #segmk.add_site_tag( + # site, 'COMPENSATION.ZHOLD_NO_CLKIN_BUF', match and \ + # not bufg_on_clkin + # ) + #segmk.add_site_tag( + # site, 'COMPENSATION.ZHOLD_NO_CLKIN_BUF_NO_TOP', match and \ + # not bufg_on_clkin and \ + # site != "PLLE2_ADV_X0Y3" and site != "PLLE2_ADV_X0Y0" + # ) + #segmk.add_site_tag( + # site, 'COMP.ZHOLD_NO_CLKIN_BUF_TOP', match and \ + # not bufg_on_clkin and \ + # (site == "PLLE2_ADV_X0Y3" or site == "PLLE2_ADV_X0Y0") + # ) + + # No INTERNAL as it has conflicting bits + #for opt in ['ZHOLD', 'BUF_IN', 'EXTERNAL']: + # if opt in ['BUF_IN', 'EXTERNAL']: + # if ps['clkfbin_conn'] not in ['', 'clk']: + # continue + # + # if site == "PLLE2_ADV_X0Y2" and opt == 'ZHOLD': + # segmk.add_site_tag( + # site, 'TOP.COMPENSATION.' + opt, + # verilog.unquote(ps['COMPENSATION']) == opt) + # else: + # segmk.add_site_tag( + # site, 'COMPENSATION.' + opt, + # verilog.unquote(ps['COMPENSATION']) == opt) + # segmk.add_site_tag( + # site, 'COMPENSATION.Z_' + opt, + # verilog.unquote(ps['COMPENSATION']) != opt) + + +# This one has bits that are in conflict with clock routing +# segmk.add_site_tag( +# site, 'COMPENSATION.INTERNAL', +# verilog.unquote(ps['COMPENSATION']) in ['INTERNAL']) + + for param in ['CLKFBOUT_MULT_F']: + paramadj = int(ps[param]) + bitstr = [int(x) for x in "{0:09b}".format(paramadj)[::-1]] + for i in range(7): + segmk.add_site_tag(site, '%s[%u]' % (param, i), bitstr[i]) + + for param in ['CLKOUT0_DUTY_CYCLE']: + assert ps[param][:2] == '0.', ps[param] + assert len(ps[param]) == 5 + paramadj = int(ps[param][2:]) + bitstr = [int(x) for x in "{0:011b}".format(paramadj)[::-1]] + + for i in range(10): + segmk.add_site_tag(site, '%s[%u]' % (param, i), bitstr[i]) + + for param, bits in [ + ('CLKOUT0_DIVIDE_F', 7), + ('CLKOUT1_DIVIDE', 7), + ('CLKOUT2_DIVIDE', 7), + ('CLKOUT3_DIVIDE', 7), + ('CLKOUT4_DIVIDE', 7), + ('CLKOUT5_DIVIDE', 7), + ('CLKOUT6_DIVIDE', 7), + ('DIVCLK_DIVIDE', 6), + ]: + # 1-128 => 0-127 for actual 7 bit value + paramadj = int(ps[param]) + if paramadj < 4: + continue + + bitstr = [int(x) for x in "{0:09b}".format(paramadj)[::-1]] + for i in range(bits): + segmk.add_site_tag(site, '%s[%u]' % (param, i), bitstr[i]) + + segmk.add_site_tag( + site, 'STARTUP_WAIT', + verilog.unquote(ps['STARTUP_WAIT']) == 'TRUE') def run(): @@ -77,14 +178,9 @@ def run(): f.readline() for l in f: j = json.loads(l) - ps = j['params'] - assert j['module'] == 'my_MMCME2_ADV' - site = verilog.unquote(ps['LOC']) + bus_tags(segmk, j, j['site']) - clkout_tags(segmk, ps, site) - misc_tags(segmk, ps, site) - - segmk.compile() + segmk.compile(bitfilter=bitfilter) segmk.write() diff --git a/fuzzers/031-cmt-mmcm/generate.tcl b/fuzzers/031-cmt-mmcm/generate.tcl index c0508a0a..63f7d140 100644 --- a/fuzzers/031-cmt-mmcm/generate.tcl +++ b/fuzzers/031-cmt-mmcm/generate.tcl @@ -5,36 +5,113 @@ # https://opensource.org/licenses/ISC # # SPDX-License-Identifier: ISC +source "$::env(XRAY_DIR)/utils/utils.tcl" + +proc make_manual_routes {filename} { + puts "MANROUTE: Loading routes from $filename" + + set fp [open $filename r] + foreach line [split [read $fp] "\n"] { + if {$line eq ""} { + continue + } + + puts "MANROUTE: Line: $line" + + # Parse the line + set fields [split $line " "] + set net_name [lindex $fields 0] + set wire_name [lindex $fields 1] + + # Check if that net exist + if {[get_nets $net_name] eq ""} { + puts "MANROUTE: net $net_name does not exist" + continue + } + + # Make the route + set status [route_via $net_name [list $wire_name] 0] + + # Failure, skip manual routing of this net + if { $status != 1 } { + puts "MANROUTE: Manual routing failed!" + set net [get_nets $net_name] + set_property -quiet FIXED_ROUTE "" $net + set_property IS_ROUTE_FIXED 0 $net + continue + } + + puts "MANROUTE: Success!" + } +} + create_project -force -part $::env(XRAY_PART) design design read_verilog top.v synth_design -top top -set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports clk] -set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports stb] -set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_02) IOSTANDARD LVCMOS33" [get_ports di] -set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_03) IOSTANDARD LVCMOS33" [get_ports do] - -create_pblock roi - -add_cells_to_pblock [get_pblocks roi] [get_cells roi] -resize_pblock [get_pblocks roi] -add "$::env(XRAY_ROI)" - set_property CFGBVS VCCO [current_design] set_property CONFIG_VOLTAGE 3.3 [current_design] set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design] -set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF] +create_clock -period 10.00 [get_ports clkin1*] +create_clock -period 10.00 [get_ports clkin2*] + # Disable MMCM frequency etc sanity checks set_property IS_ENABLED 0 [get_drc_checks {PDRC-29}] set_property IS_ENABLED 0 [get_drc_checks {PDRC-30}] +set_property IS_ENABLED 0 [get_drc_checks {PDRC-34}] set_property IS_ENABLED 0 [get_drc_checks {AVAL-50}] set_property IS_ENABLED 0 [get_drc_checks {AVAL-53}] -set_property IS_ENABLED 0 [get_drc_checks {REQP-126}] +set_property IS_ENABLED 0 [get_drc_checks {REQP-123}] +set_property IS_ENABLED 0 [get_drc_checks {REQP-119}] # PLL +set_property IS_ENABLED 0 [get_drc_checks {PDRC-43}] set_property IS_ENABLED 0 [get_drc_checks {REQP-161}] +set_property IS_ENABLED 0 [get_drc_checks {AVAL-78}] + +set_property IS_ENABLED 0 [get_drc_checks {UCIO-1}] +set_property IS_ENABLED 0 [get_drc_checks {NSTD-1}] place_design -route_design +write_checkpoint -force design_placed.dcp + +make_manual_routes routes.txt +write_checkpoint -force design_pre_route.dcp + +route_design -directive Quick -preserve + +set unrouted_nets [get_nets -filter {ROUTE_STATUS!="ROUTED"}] +if {[llength $unrouted_nets] ne 0} { + puts "MANROUTE: Got unrouted nets: $unrouted_nets" + puts "MANROUTE: Ripping up and starting again with no fixed routes" + + route_design -unroute + + set fixed_nets [get_nets -filter {IS_ROUTE_FIXED==1}] + if {[llength $fixed_nets] ne 0} { + set_property FIXED_ROUTE "" $fixed_nets + set_property IS_ROUTE_FIXED 0 $fixed_nets + } + + route_design -directive Quick +} write_checkpoint -force design.dcp write_bitstream -force design.bit + +set fp [open params.json "w"] +puts $fp "\[" +foreach cell [get_cells -hierarchical -filter {REF_NAME == PLLE2_ADV}] { + puts $fp " {" + puts $fp " \"tile\": \"[get_tiles -of [get_sites -of $cell]]\"," + puts $fp " \"site\": \"[get_sites -of $cell]\"," + puts $fp " \"params\": {" + foreach prop [list_property $cell] { + puts $fp " \"$prop\": \"[get_property $prop $cell]\"," + } + puts $fp " }" + puts $fp " }," + +} +puts $fp "\]" +close $fp diff --git a/fuzzers/031-cmt-mmcm/top.py b/fuzzers/031-cmt-mmcm/top.py index a49657c3..57eeef16 100644 --- a/fuzzers/031-cmt-mmcm/top.py +++ b/fuzzers/031-cmt-mmcm/top.py @@ -13,160 +13,300 @@ import random random.seed(int(os.getenv("SEED"), 16)) from prjxray import util from prjxray import verilog -from prjxray.verilog import vrandbit, vrandbits -import sys +from prjxray.db import Database import json -import numpy as np def gen_sites(): - for _tile_name, site_name, _site_type in sorted(util.get_roi().gen_sites( - ["MMCME2_ADV"])): - yield site_name + 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) + + tile_type = tile_name.rsplit("_", 1)[0] + + for site_name, site_type in gridinfo.sites.items(): + if site_type in ['MMCME2_ADV']: + yield tile_name, tile_type, site_name -sites = list(gen_sites()) -DUTN = len(sites) -DIN_N = DUTN * 8 -DOUT_N = DUTN * 8 +def main(): + sites = sorted(list(gen_sites())) + max_sites = len(sites) -verilog.top_harness(DIN_N, DOUT_N) + f = open('params.jl', 'w') + f.write('module,loc,params\n') -f = open('params.jl', 'w') -f.write('module,loc,params\n') -print( - 'module roi(input clk, input [%d:0] din, output [%d:0] dout);' % - (DIN_N - 1, DOUT_N - 1)) + routes_file = open('routes.txt', 'w') -for loci, site in enumerate(sites): - - ports = { - 'clk': 'clk', - 'din': 'din[ %d +: 8]' % (8 * loci, ), - 'dout': 'dout[ %d +: 8]' % (8 * loci, ), - } - - params = { - "CLKOUT1_DIVIDE": - int( - np.random.choice( - [1, 63, 127, 128, random.randint(2, 127)], - p=[0.2, 0.1, 0.1, 0.1, - 0.5])), # make sure that special values are present - "STARTUP_WAIT": - random.choice(["\"TRUE\"", "\"FALSE\""]), - "CLKOUT4_CASCADE": - random.choice(["\"TRUE\"", "\"FALSE\""]), - "STARTUP_WAIT": - random.choice(["\"TRUE\"", "\"FALSE\""]), - "CLKFBOUT_USE_FINE_PS": - random.choice(["\"TRUE\"", "\"FALSE\""]), - "CLKOUT0_USE_FINE_PS": - random.choice(["\"TRUE\"", "\"FALSE\""]), - "CLKOUT1_USE_FINE_PS": - random.choice(["\"TRUE\"", "\"FALSE\""]), - "CLKOUT2_USE_FINE_PS": - random.choice(["\"TRUE\"", "\"FALSE\""]), - "CLKOUT3_USE_FINE_PS": - random.choice(["\"TRUE\"", "\"FALSE\""]), - "CLKOUT4_USE_FINE_PS": - random.choice(["\"TRUE\"", "\"FALSE\""]), - "CLKOUT5_USE_FINE_PS": - random.choice(["\"TRUE\"", "\"FALSE\""]), - "CLKOUT6_USE_FINE_PS": - random.choice(["\"TRUE\"", "\"FALSE\""]), - } - - modname = "my_MMCME2_ADV" - verilog.instance(modname, "inst_%u" % loci, ports, params=params) - # LOC isn't supported - params["LOC"] = verilog.quote(site) - - j = {'module': modname, 'i': loci, 'params': params} - f.write('%s\n' % (json.dumps(j))) - print('') - -f.close() -print( - '''endmodule - -// --------------------------------------------------------------------- - -''') - -print( - ''' -module my_MMCME2_ADV (input clk, input [7:0] din, output [7:0] dout); - parameter CLKOUT1_DIVIDE = 1; - parameter CLKOUT2_DIVIDE = 1; - parameter CLKOUT3_DIVIDE = 1; - parameter CLKOUT4_DIVIDE = 1; - parameter CLKOUT5_DIVIDE = 1; - parameter CLKOUT6_DIVIDE = 1; - parameter DIVCLK_DIVIDE = 1; - parameter CLKFBOUT_MULT = 5; - parameter CLKOUT4_CASCADE = "FALSE"; - parameter STARTUP_WAIT = "FALSE"; - parameter CLKFBOUT_USE_FINE_PS = "FALSE"; - parameter CLKOUT0_USE_FINE_PS = "FALSE"; - parameter CLKOUT1_USE_FINE_PS = "FALSE"; - parameter CLKOUT2_USE_FINE_PS = "FALSE"; - parameter CLKOUT3_USE_FINE_PS = "FALSE"; - parameter CLKOUT4_USE_FINE_PS = "FALSE"; - parameter CLKOUT5_USE_FINE_PS = "FALSE"; - parameter CLKOUT6_USE_FINE_PS = "FALSE"; + print( + """ +module top( + input [{N}:0] clkin1, + input [{N}:0] clkin2, + input [{N}:0] clkfb, + input [{N}:0] dclk +); (* KEEP, DONT_TOUCH *) + LUT1 dummy(); +""".format(N=max_sites - 1)) + + for i, ( + tile_name, + tile_type, + site, + ) in enumerate(sorted(gen_sites())): + params = { + "site": + site, + 'active': + random.random() > .2, + "clkin1_conn": + random.choice( + ("clkfbout_mult_BUFG_" + site, "clkin1[{}]".format(i), "")), + "clkin2_conn": + random.choice( + ("clkfbout_mult_BUFG_" + site, "clkin2[{}]".format(i), "")), + "dclk_conn": + random.choice(( + "0", + "dclk[{}]".format(i), + )), + "dwe_conn": + random.choice(( + "", + "1", + "0", + "dwe_" + site, + "den_" + site, + )), + "den_conn": + random.choice(( + "", + "1", + "0", + "den_" + site, + )), + "daddr4_conn": + random.choice(( + "0", + "dwe_" + site, + )), + "IS_RST_INVERTED": + random.randint(0, 1), + "IS_PWRDWN_INVERTED": + random.randint(0, 1), + "IS_CLKINSEL_INVERTED": + random.randint(0, 1), + "CLKFBOUT_MULT_F": + random.randint(2, 4), + "CLKOUT0_DIVIDE_F": + random.randint(1, 128), + "CLKOUT1_DIVIDE": + random.randint(1, 128), + "CLKOUT2_DIVIDE": + random.randint(1, 128), + "CLKOUT3_DIVIDE": + random.randint(1, 128), + "CLKOUT4_DIVIDE": + random.randint(1, 128), + "CLKOUT5_DIVIDE": + random.randint(1, 128), + "CLKOUT6_DIVIDE": + random.randint(1, 128), + "DIVCLK_DIVIDE": + random.randint(1, 5), + "CLKOUT0_DUTY_CYCLE": + "0.500", + "STARTUP_WAIT": + verilog.quote('TRUE' if random.randint(0, 1) else 'FALSE'), + "COMPENSATION": + verilog.quote( + random.choice(( + 'ZHOLD', + 'BUF_IN', + 'EXTERNAL', + 'INTERNAL', + ))), + "BANDWIDTH": + verilog.quote(random.choice(( + 'OPTIMIZED', + 'HIGH', + 'LOW', + ))), + } + + if verilog.unquote(params['COMPENSATION']) == 'ZHOLD': + params['clkfbin_conn'] = random.choice( + ( + "", + "clkfbout_mult_BUFG_" + site, + )) + elif verilog.unquote(params['COMPENSATION']) == 'INTERNAL': + params['clkfbin_conn'] = random.choice( + ( + "", + "clkfbout_mult_" + site, + )) + else: + params['clkfbin_conn'] = random.choice( + ("", "clkfb[{}]".format(i), "clkfbout_mult_BUFG_" + site)) + + params['clkin1_route'] = random.choice( + ( + "{}_CLKIN1", + "{}_FREQ_BB0", + "{}_FREQ_BB1", + "{}_FREQ_BB2", + "{}_FREQ_BB3", + "{}_MMCME2_CLK_IN1_INT", + )).format(tile_type) + + params['clkin2_route'] = random.choice( + ( + "{}_CLKIN2", + "{}_FREQ_BB0", + "{}_FREQ_BB1", + "{}_FREQ_BB2", + "{}_FREQ_BB3", + "{}_MMCME2_CLK_IN2_INT", + )).format(tile_type) + + params['clkfbin_route'] = random.choice( + ( + "{}_CLKFBOUT2IN", + "{}_UPPER_T_FREQ_BB0", + "{}_UPPER_T_FREQ_BB1", + "{}_UPPER_T_FREQ_BB2", + "{}_UPPER_T_FREQ_BB3", + "{}_UPPER_T_MMCME2_CLK_FB_INT", + )).format(tile_type.replace("_UPPER_T", "")) + + f.write('%s\n' % (json.dumps(params))) + + def make_ibuf_net(net): + p = net.find('[') + return net[:p] + '_IBUF' + net[p:] + + if params['clkin1_conn'] != "": + net = make_ibuf_net(params['clkin1_conn']) + wire = '{}/{}'.format(tile_name, params['clkin1_route']) + routes_file.write('{} {}\n'.format(net, wire)) + + if params['clkin2_conn'] != "": + net = make_ibuf_net(params['clkin2_conn']) + wire = '{}/{}'.format(tile_name, params['clkin2_route']) + routes_file.write('{} {}\n'.format(net, wire)) + + if params['clkfbin_conn'] != "" and\ + params['clkfbin_conn'] != ("clkfbout_mult_BUFG_" + site): + net = params['clkfbin_conn'] + if "[" in net and "]" in net: + net = make_ibuf_net(net) + wire = '{}/{}'.format(tile_name, params['clkfbin_route']) + routes_file.write('{} {}\n'.format(net, wire)) + + if not params['active']: + continue + + print( + """ + + wire den_{site}; + wire dwe_{site}; + + LUT1 den_lut_{site} ( + .O(den_{site}) + ); + + LUT1 dwe_lut_{site} ( + .O(dwe_{site}) + ); + + wire clkfbout_mult_{site}; + wire clkfbout_mult_BUFG_{site}; + wire clkout0_{site}; + wire clkout1_{site}; + wire clkout2_{site}; + wire clkout3_{site}; + wire clkout4_{site}; + wire clkout5_{site}; + wire clkout6_{site}; + (* KEEP, DONT_TOUCH, LOC = "{site}" *) MMCME2_ADV #( - .CLKOUT1_DIVIDE(CLKOUT1_DIVIDE), - .CLKOUT2_DIVIDE(CLKOUT2_DIVIDE), - .CLKOUT3_DIVIDE(CLKOUT3_DIVIDE), - .CLKOUT4_DIVIDE(CLKOUT4_DIVIDE), - .CLKOUT5_DIVIDE(CLKOUT5_DIVIDE), - .CLKOUT6_DIVIDE(CLKOUT6_DIVIDE), - .CLKOUT4_CASCADE(CLKOUT4_CASCADE), - .STARTUP_WAIT(STARTUP_WAIT), - .CLKFBOUT_USE_FINE_PS(CLKFBOUT_USE_FINE_PS), - .CLKOUT0_USE_FINE_PS(CLKOUT0_USE_FINE_PS), - .CLKOUT1_USE_FINE_PS(CLKOUT1_USE_FINE_PS), - .CLKOUT2_USE_FINE_PS(CLKOUT2_USE_FINE_PS), - .CLKOUT3_USE_FINE_PS(CLKOUT3_USE_FINE_PS), - .CLKOUT4_USE_FINE_PS(CLKOUT4_USE_FINE_PS), - .CLKOUT5_USE_FINE_PS(CLKOUT5_USE_FINE_PS), - .CLKOUT6_USE_FINE_PS(CLKOUT6_USE_FINE_PS) - ) dut( - .CLKFBOUT(), - .CLKFBOUTB(), - .CLKFBSTOPPED(), - .CLKINSTOPPED(), - .CLKOUT0(dout[0]), - .CLKOUT0B(), - .CLKOUT1(), - .CLKOUT1B(), - .CLKOUT2(), - .CLKOUT2B(), - .CLKOUT3(), - .CLKOUT3B(), - .CLKOUT4(), - .CLKOUT5(), - .CLKOUT6(), - .DO(), + .IS_RST_INVERTED({IS_RST_INVERTED}), + .IS_PWRDWN_INVERTED({IS_PWRDWN_INVERTED}), + .IS_CLKINSEL_INVERTED({IS_CLKINSEL_INVERTED}), + .CLKOUT0_DIVIDE_F({CLKOUT0_DIVIDE_F}), + .CLKOUT1_DIVIDE({CLKOUT1_DIVIDE}), + .CLKOUT2_DIVIDE({CLKOUT2_DIVIDE}), + .CLKOUT3_DIVIDE({CLKOUT3_DIVIDE}), + .CLKOUT4_DIVIDE({CLKOUT4_DIVIDE}), + .CLKOUT5_DIVIDE({CLKOUT5_DIVIDE}), + .CLKOUT6_DIVIDE({CLKOUT6_DIVIDE}), + .CLKFBOUT_MULT_F({CLKFBOUT_MULT_F}), + .DIVCLK_DIVIDE({DIVCLK_DIVIDE}), + .STARTUP_WAIT({STARTUP_WAIT}), + .CLKOUT0_DUTY_CYCLE({CLKOUT0_DUTY_CYCLE}), + .COMPENSATION({COMPENSATION}), + .BANDWIDTH({BANDWIDTH}), + .CLKIN1_PERIOD(10.0), + .CLKIN2_PERIOD(10.0) + ) pll_{site} ( + .CLKFBOUT(clkfbout_mult_{site}), + .CLKOUT0(clkout0_{site}), + .CLKOUT1(clkout1_{site}), + .CLKOUT2(clkout2_{site}), + .CLKOUT3(clkout3_{site}), + .CLKOUT4(clkout4_{site}), + .CLKOUT5(clkout5_{site}), + .CLKOUT6(clkout6_{site}), .DRDY(), .LOCKED(), - .PSDONE(), - .CLKFBIN(clk), - .CLKIN1(clk), - .CLKIN2(clk), - .CLKINSEL(clk), - .DADDR(), - .DCLK(clk), - .DEN(), - .DI(), - .DWE(), - .PSCLK(clk), - .PSEN(), - .PSINCDEC(), + .DO(), + .CLKFBIN({clkfbin_conn}), + .CLKIN1({clkin1_conn}), + .CLKIN2({clkin2_conn}), + .CLKINSEL(), + .DCLK({dclk_conn}), + .DEN({den_conn}), + .DWE({dwe_conn}), .PWRDWN(), - .RST(din[0])); -endmodule -''') + .RST(), + .DI(), + .DADDR({{7{{ {daddr4_conn} }} }})); + + (* KEEP, DONT_TOUCH *) + BUFG bufg_{site} ( + .I(clkfbout_mult_{site}), + .O(clkfbout_mult_BUFG_{site}) + ); + + (* KEEP, DONT_TOUCH *) + FDRE reg_clkfbout_mult_{site} ( + .C(clkfbout_mult_{site}) + ); + """.format(**params)) + + disabled_clkout = random.randint(0, 7) + for clk in range(0, 7): + if clk == disabled_clkout: + continue + + print( + """ + (* KEEP, DONT_TOUCH *) + FDRE reg_clkout{clk}_{site} ( + .C(clkout{clk}_{site}) + ); + """.format(clk=clk, site=params['site'])) + + print('endmodule') + + f.close() + + +if __name__ == "__main__": + main() diff --git a/fuzzers/031-cmt-mmcm/write_mmcm_reg.py b/fuzzers/031-cmt-mmcm/write_mmcm_reg.py new file mode 100644 index 00000000..8a8afe13 --- /dev/null +++ b/fuzzers/031-cmt-mmcm/write_mmcm_reg.py @@ -0,0 +1,316 @@ +#!/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 argparse + +REGISTER_LAYOUT = { + 'CLKOUT1': [ + ('LOW_TIME', 6), + ('HIGH_TIME', 6), + ('OUTPUT_ENABLE', 1), + ('PHASE_MUX', 3), + ], + 'CLKOUT2': [ + ('DELAY_TIME', 6), + ('NO_COUNT', 1), + ('EDGE', 1), + ('MX', 2), + ('FRAC_WF_R', 1), + ('FRAC_EN', 1), + ('FRAC', 3), + ('RESERVED', 1), + ], + 'CLKOUT2_FRACTIONAL': [ + ('DELAY_TIME', 6), + ('NO_COUNT', 1), + ('EDGE', 1), + ('MX', 2), + ('FRAC_WF_F', 1), + ('PHASE_MUX_F', 3), + ('RESERVED', 2), + ], + 'DIVCLK': [ + ('LOW_TIME', 6), + ('HIGH_TIME', 6), + ('NO_COUNT', 1), + ('EDGE', 1), + ('RESERVED', 2), + ], + 'LOCKREG1': [ + ('LKTABLE', 10, 20), + ('LOCKREG1_RESERVED', 6, 0), + ], + 'LOCKREG2': [ + ('LKTABLE', 10, 0), + ('LKTABLE', 5, 30), + ('LOCKREG2_RESERVED', 1, 0), + ], + 'LOCKREG3': [ + ('LKTABLE', 10, 10), + ('LKTABLE', 5, 35), + ('LOCKREG3_RESERVED', 1, 0), + ], + 'FILTREG1': [ + ('FILTREG1_RESERVED', 8, 0), + ('TABLE', 1, 6), + ('FILTREG1_RESERVED', 2, 8), + ('TABLE', 2, 7), + ('FILTREG1_RESERVED', 2, 10), + ('TABLE', 1, 9), + ], + 'FILTREG2': [ + ('FILTREG2_RESERVED', 4, 0), + ('TABLE', 1, 0), + ('FILTREG2_RESERVED', 2, 4), + ('TABLE', 2, 1), + ('FILTREG2_RESERVED', 2, 6), + ('TABLE', 2, 3), + ('FILTREG2_RESERVED', 2, 8), + ('TABLE', 1, 5), + ], + 'POWER_REG': [ + ('POWER_REG', 16), + ], +} + +BASE_OFFSET = 0x00 +REGISTER_MAP = [] + +REGISTER_MAP.append(None) +REGISTER_MAP.append(None) + +for idx in range(3): + REGISTER_MAP.append(None) + +REGISTER_MAP.append(None) + +# 0x06 - 0x15 +for output in ['CLKOUT5', 'CLKOUT0', 'CLKOUT1', 'CLKOUT2', 'CLKOUT3', + 'CLKOUT4', 'CLKOUT6', 'CLKFBOUT']: + if output is not None: + REGISTER_MAP.append(('CLKOUT1', output)) + if output in ['CLKOUT5', 'CLKOUT6']: + # CLKOUT5 CLKOUT2 register actually controls the fractional of + # CLKOUT0. + # CLKOUT6 CLKOUT2 register actually controls the fractional of + # CLKFBOUT. + REGISTER_MAP.append(('CLKOUT2_FRACTIONAL', output)) + else: + REGISTER_MAP.append(('CLKOUT2', output)) + else: + REGISTER_MAP.append(None) + REGISTER_MAP.append(None) + +# 0x16 +REGISTER_MAP.append(('DIVCLK', 'DIVCLK')) +# 0x17 +REGISTER_MAP.append(None) +# 0x18-0x1A +REGISTER_MAP.append(('LOCKREG1', 'LOCKREG1')) +REGISTER_MAP.append(('LOCKREG2', 'LOCKREG2')) +REGISTER_MAP.append(('LOCKREG3', 'LOCKREG3')) + +for _ in range(0x28 - 0x1A - 1): + REGISTER_MAP.append(None) + +REGISTER_MAP.append(('POWER_REG', 'POWER_REG')) + +for _ in range(0x4E - 0x28 - 1): + REGISTER_MAP.append(None) + +# 0x4E - 0x4F +REGISTER_MAP.append(('FILTREG1', 'FILTREG1')) +REGISTER_MAP.append(('FILTREG2', 'FILTREG2')) + +for _ in range(0x20): + REGISTER_MAP.append(None) + + +class RegisterAddress(object): + def __init__(self, frame_offsets, bit_offset, reverse=False): + self.frame_index = 0 + self.frame_offsets = frame_offsets + self.bit_offset = bit_offset + self.bits_used = set() + self.reverse = reverse + + def next_bit(self, used=True): + output = '{}_{}'.format( + self.frame_offsets[self.frame_index], self.bit_offset) + + if used: + self.bits_used.add(output) + + self.frame_index += 1 + if self.frame_index >= len(self.frame_offsets): + self.frame_index = 0 + if self.reverse: + self.bit_offset -= 1 + else: + self.bit_offset += 1 + + return output + + +def passthrough_non_register_segbits(seg_in): + """ Filter input segbits file and capture register base offset. + + Some MMCM bit ranges are documented registers in the PLL/MMCM dynamic + reconfiguration iterface. These features will be generated in + output_registers. In order for output_registers to function, it needs + the starting bit offset of the register space, which is based off of + base_offset_register segbit definition. + + Other features generated in fuzzing are passed through. + + """ + base_offset_register = 'CMT_LOWER_B.MMCME2.CLKOUT5_DIVIDE[1]' + + bit_offset = None + in_use = None + with open(seg_in, 'r') as f: + for l in f: + if l.startswith(base_offset_register): + parts = l.split() + assert len(parts) == 2 + assert parts[0] == base_offset_register + frame_offset, bit_index = map(int, parts[1].split('_')) + + assert frame_offset == 29 + assert bit_index > 3 + bit_offset = bit_index + 3 + 3 * 16 + + continue + + if 'IN_USE' in l: + assert in_use is None + in_use = l.strip() + continue + + parts = l.split() + feature_parts = parts[0].split('.') + + if len(feature_parts) < 3: + print(l.strip()) + continue + + if feature_parts[2] == 'BANDWIDTH': + continue + + if '[' not in feature_parts[2]: + print(l.strip()) + continue + + base_feature = feature_parts[2].split('[') + + if base_feature[0] in [ + 'CLKOUT0_DIVIDE_F', + 'CLKOUT1_DIVIDE', + 'CLKOUT2_DIVIDE', + 'CLKOUT3_DIVIDE', + 'CLKOUT4_DIVIDE', + 'CLKOUT5_DIVIDE', + 'CLKOUT6_DIVIDE', + 'DIVCLK_DIVIDE', + 'CLKFBOUT_MULT_F', + 'CLKOUT0_DUTY_CYCLE', + ]: + # These features are MMCM registers, so ignore the base + continue + + print(l.strip()) + + assert bit_offset is not None + assert in_use is not None + return bit_offset, in_use + + +def output_registers(bit_offset, in_use): + """ Output segbits for the known MMCM register space. + + The first bit offset in the register space is required to generate this + output. + + """ + reg = RegisterAddress( + frame_offsets=[29, 28], bit_offset=bit_offset, reverse=True) + + for idx, register in enumerate(REGISTER_MAP): + if register is None: + for _ in range(16): + reg.next_bit(used=False) + continue + + layout, register_name = register + + layout_bits = REGISTER_LAYOUT[layout] + + simple_layout = len(layout_bits[0]) == 2 + + if True: + bit_count = 0 + if simple_layout: + for field, width in layout_bits: + for bit in range(width): + bit_count += 1 + + if field is None: + reg.next_bit(used=False) + continue + + print( + 'CMT_LOWER_B.MMCME2.{}_{}_{}[{}] {}'.format( + register_name, layout, field, bit, + reg.next_bit())) + else: + for field, width, start_bit in layout_bits: + for bit in range(width): + bit_count += 1 + + if field is None: + reg.next_bit(used=False) + continue + + print( + 'CMT_LOWER_B.MMCME2.{}[{}] {}'.format( + field, start_bit + bit, reg.next_bit())) + + assert bit_count == 16 + else: + for bit in range(16): + if register_name != layout or layout in ['CLKOUT1', 'CLKOUT2']: + print( + 'CMT_LOWER_B.MMCME2.{}_{}[{}] {}'.format( + register_name, layout, bit, reg.next_bit())) + else: + print( + 'CMT_LOWER_B.MMCME2.{}[{}] {}'.format( + register_name, bit, reg.next_bit())) + + parts = in_use.split() + feature = parts[0] + bits = [p for p in parts[1:] if p not in reg.bits_used] + print('{} {}'.format(feature, ' '.join(bits))) + + +def main(): + parser = argparse.ArgumentParser(description="") + + parser.add_argument('--seg_in') + + args = parser.parse_args() + + bit_offset, in_use = passthrough_non_register_segbits(args.seg_in) + + output_registers(bit_offset, in_use) + + +if __name__ == "__main__": + main() diff --git a/fuzzers/034b-cmt-mmcm-pips/Makefile b/fuzzers/034b-cmt-mmcm-pips/Makefile new file mode 100644 index 00000000..d16f9db5 --- /dev/null +++ b/fuzzers/034b-cmt-mmcm-pips/Makefile @@ -0,0 +1,55 @@ +# 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 +export FUZDIR=$(shell pwd) +PIP_TYPE?=cmt_top_lower +PIPLIST_TCL=$(FUZDIR)/cmt_top_lower_b.tcl +TODO_RE=".*CMT_LR_LOWER_B_MMCM_CLK(IN1|IN2|FBIN)\.CMT_[LR]" + +MAKETODO_FLAGS=--sides "r_lower_b,l_lower_b" --pip-dir ${FUZDIR}/../piplist/build/cmt_top_lower --pip-type cmt_top --seg-type cmt_top --re $(TODO_RE) + +N = 200 +SEGMATCH_FLAGS=-m 10 -M 20 -c 300 + +A_PIPLIST=cmt_top_l_lower_b.txt + +include ../pip_loop.mk + +build/segbits_cmt_top_l_lower_b.rdb: $(SPECIMENS_OK) + ${XRAY_SEGMATCH} ${SEGMATCH_FLAGS} -o build/segbits_cmt_top_lr_lower_b.rdb \ + $(shell find build -name segdata_cmt_top_l_lower_b.txt) \ + $(shell find build -name segdata_cmt_top_r_lower_b.txt) + sed < build/segbits_cmt_top_lr_lower_b.rdb > build/segbits_cmt_top_l_lower_b.rdb -e 's/CMT_LRMAP_LOWER_B/CMT_L_LOWER_B/' + sed < build/segbits_cmt_top_lr_lower_b.rdb > build/segbits_cmt_top_r_lower_b.rdb -e 's/CMT_LRMAP_LOWER_B/CMT_R_LOWER_B/' + +RDBS = build/segbits_cmt_top_l_lower_b.rdb + +database: ${RDBS} + python3 ${FUZDIR}/fixup_and_group.py -g tag_groups.txt \ + -i build/segbits_cmt_top_l_lower_b.rdb \ + -o build/segbits_cmt_top_l_lower_b.db + + python3 ${FUZDIR}/fixup_and_group.py -g tag_groups.txt \ + -i build/segbits_cmt_top_r_lower_b.rdb \ + -o build/segbits_cmt_top_r_lower_b.db + + # Keep a copy to track iter progress + cp build/segbits_cmt_top_l_lower_b.rdb build/$(ITER)/segbits_cmt_top_l_lower_b.rdb + cp build/segbits_cmt_top_l_lower_b.db build/$(ITER)/segbits_cmt_top_l_lower_b.db + cp build/segbits_cmt_top_r_lower_b.rdb build/$(ITER)/segbits_cmt_top_r_lower_b.rdb + cp build/segbits_cmt_top_r_lower_b.db build/$(ITER)/segbits_cmt_top_r_lower_b.db + + # Clobber existing .db to eliminate potential conflicts + cp ${XRAY_DATABASE_DIR}/${XRAY_DATABASE}/segbits*.db build/database/${XRAY_DATABASE} + XRAY_DATABASE_DIR=${FUZDIR}/build/database ${XRAY_MERGEDB} cmt_top_l_lower_b build/segbits_cmt_top_l_lower_b.db + XRAY_DATABASE_DIR=${FUZDIR}/build/database ${XRAY_MERGEDB} cmt_top_r_lower_b build/segbits_cmt_top_r_lower_b.db + +pushdb: database + ${XRAY_MERGEDB} cmt_top_l_lower_b build/segbits_cmt_top_l_lower_b.db + ${XRAY_MERGEDB} cmt_top_r_lower_b build/segbits_cmt_top_r_lower_b.db + +.PHONY: database pushdb diff --git a/fuzzers/034b-cmt-mmcm-pips/README.md b/fuzzers/034b-cmt-mmcm-pips/README.md new file mode 100644 index 00000000..899fb995 --- /dev/null +++ b/fuzzers/034b-cmt-mmcm-pips/README.md @@ -0,0 +1,20 @@ +# Fuzzer for the PIPs of CMT_TOP_[LR]_LOWER_B tiles. + +The fuzzer instantiates a MMCM in each available site with 2/3 probability of using it. Once used it is connected randomly to various clock and logic resources. + +For some nets a randomized "manual" route is chosen to cover as many routing scenarios as possible. + +The information whether a MMCM is used or not is stored in a file (`"design.txt"`) along with the randomized route (`route.txt`) + +After the design synthesis the `generate.py` sets fixed routes on some nets which is read from the `route.txt` file. The rest of the design is routed in the regular way. The script also dumps all used PIPs (as reported by Vivado) to the `design_pips.txt`. + +The tag generation is done in the following way: + +- If a MMCM site is occupied then tags for all active PIPs are emitted as 1s. No tags are emitted for inactive PIPs. +- When a MMCM site is not occupied (IN_USE=0) then tags for all PIPs for the CMT tile are emitted as 0s. +- The IN_USE tag is emitted directly. + +The raw solution of tag bits is postprocessed via the custom script `fixup_and_group.py`. The script does two things: + +- Clears all bits found for the IN_USE tag in all other tags. Those bits are common to all of them. +- Groups tags according to the group definitions read from the `tag_groups.txt` file. Bits that are common to the group are set as 0 in each tag that belongs to it (tags within a group are exclusive). diff --git a/fuzzers/034b-cmt-mmcm-pips/cmt_top_lower_b.tcl b/fuzzers/034b-cmt-mmcm-pips/cmt_top_lower_b.tcl new file mode 100644 index 00000000..fe73d0db --- /dev/null +++ b/fuzzers/034b-cmt-mmcm-pips/cmt_top_lower_b.tcl @@ -0,0 +1,46 @@ +# 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 +proc print_tile_pips {tile_type filename} { + set tile [lindex [get_tiles -filter "TYPE == $tile_type"] 0] + puts "Dumping PIPs for tile $tile ($tile_type) to $filename." + set fp [open $filename w] + foreach pip [lsort [get_pips -of_objects [get_tiles $tile]]] { + set src [get_wires -uphill -of_objects $pip] + set dst [get_wires -downhill -of_objects $pip] + if {[llength [get_nodes -uphill -of_objects [get_nodes -of_objects $dst]]] == 1} { + set src_node [get_nodes -of $src] + set dst_node [get_nodes -of $dst] + + if { [string first INT_INTERFACE [get_wires -of $src_node]] != -1 } { + continue + } + if { [string first INT_INTERFACE [get_wires -of $dst_node]] != -1 } { + continue + } + } + puts $fp "$tile_type.[regsub {.*/} $dst ""].[regsub {.*/} $src ""] [get_property IS_DIRECTIONAL $pip]" + } + close $fp +} + +proc print_tile_wires {tile_type filename} { + set tile [lindex [get_tiles -filter "TYPE == $tile_type"] 0] + set fp [open $filename w] + foreach wire [lsort [get_wires -of_objects [get_tiles $tile]]] { + puts $fp "$tile_type [regsub {.*/} $wire ""]" + } +} + +create_project -force -part $::env(XRAY_PART) design design +set_property design_mode PinPlanning [current_fileset] +open_io_design -name io_1 + +print_tile_pips CMT_TOP_L_LOWER_B cmt_top_l_lower_b.txt +print_tile_pips CMT_TOP_R_LOWER_B cmt_top_r_lower_b.txt +print_tile_wires CMT_TOP_L_LOWER_B cmt_top_l_lower_b_wires.txt +print_tile_wires CMT_TOP_R_LOWER_B cmt_top_r_lower_b_wires.txt diff --git a/fuzzers/034b-cmt-mmcm-pips/fixup_and_group.py b/fuzzers/034b-cmt-mmcm-pips/fixup_and_group.py new file mode 100755 index 00000000..10e1a008 --- /dev/null +++ b/fuzzers/034b-cmt-mmcm-pips/fixup_and_group.py @@ -0,0 +1,255 @@ +#!/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 +""" +This is a db fixup script that does two things: + +1. Clears (removes) all bits found in "IN_USE" tag(s) and removes the IN_USE + tag itself + +2. Reads tag group definition from a file and applies the tag grouping. First + a set of common bits for each group is found (logical OR among all tags + belonging to the group). Then in each tag belonging to the group those + bits are set to 0 but only if they are not already present there. + +The resulting data is written into a segbits file. +""" +import argparse +import re +import itertools + +# ============================================================================= + + +def load_tag_groups(file_name): + """ + Loads tag groups from a text file. + + A tag group is defined by specifying a space separated list of tags within + a single line. Lines that are empty or start with '#' are ignored. + """ + tag_groups = [] + + # Load tag group specifications + with open(file_name, "r") as fp: + for line in fp: + line = line.strip() + + if len(line) == 0 or line.startswith("#"): + continue + + group = set(line.split()) + if len(group): + tag_groups.append(group) + + # Check if all tag groups are exclusive + for tag_group_a, tag_group_b in itertools.combinations(tag_groups, 2): + + tags = tag_group_a & tag_group_b + if len(tags): + raise RuntimeError( + "Tag(s) {} are present in multiple groups".format( + " ".join(tags))) + + return tag_groups + + +# ============================================================================= + + +def parse_bit(bit): + """ + Decodes string describing a bit. Returns a tuple (frame, bit, value) + """ + match = re.match("^(!?)([0-9]+)_([0-9]+)$", bit) + assert match != None, bit + + val = int(match.group(1) != "!") + frm = int(match.group(2)) + bit = int(match.group(3)) + + return frm, bit, val + + +def bit_to_str(bit): + """ + Converts a tuple (frame, bit, value) to its string representation. + """ + s = "!" if not bit[2] else "" + return "{}{}_{:02d}".format(s, bit[0], bit[1]) + + +def load_segbits(file_name): + """ + Loads a segbits file. + """ + + segbits = {} + + with open(file_name, "r") as fp: + for line in fp: + line = line.strip() + fields = line.split() + + if len(fields) < 2: + raise RuntimeError("Malformed line: '%s'" % line) + + tag = fields[0] + + if "<" in line or ">" in line: + segbits[tag] = set() + + else: + bits = set([parse_bit(bit) for bit in fields[1:]]) + segbits[tag] = bits + + return segbits + + +def save_segbits(file_name, segbits): + """ + Save segbits to a .db or .rdb file + """ + + with open(file_name, "w") as fp: + for tag, bits in segbits.items(): + if not len(bits): + continue + + line = tag + " " + line += " ".join([bit_to_str(bit) for bit in sorted(list(bits))]) + fp.write(line + "\n") + + +# ============================================================================= + + +def mask_out_bits(segbits, mask, tags_to_mask=None): + """ + Given a set of bits and a list of tags to affect (optional) removes all + the bits from each tag that are present (and equal) in the masking set. + """ + + if tags_to_mask is None: + tags_to_mask = segbits.keys() + + # Mask out matching bits + for tag in tags_to_mask: + bits = segbits[tag] + + bits = set(bits) - set(mask) + segbits[tag] = bits + + return segbits + + +def find_common_bits_for_tag_groups(segbits, tag_groups): + """ + For each tag group finds a common set of bits that have value of one. + """ + + bit_groups = [] + + for tag_group in tag_groups: + bit_group = set() + + for tag, bits in segbits.items(): + if tag in tag_group: + ones = set([b for b in bits if b[2]]) + bit_group |= ones + + bit_groups.append(bit_group) + + return bit_groups + + +def group_tags(segbits, tag_groups, bit_groups): + """ + Implements tag grouping. If a tag belongs to a group then the common bits + of that group are added to is as zeros. + """ + + for tag_group, bit_group in zip(tag_groups, bit_groups): + zeros = set([(b[0], b[1], 0) for b in bit_group]) + for tag in tag_group: + + # Insert zero bits + if tag in segbits.keys(): + bits = segbits[tag] + for z in zeros: + if not (z[0], z[1], 1) in bits: + bits.add(z) + + return segbits + + +# ============================================================================= + + +def main(): + + # Parse arguments + parser = argparse.ArgumentParser() + + parser.add_argument("-i", required=True, type=str, help="Input .rdb file") + parser.add_argument( + "-g", required=True, type=str, help="Input tag group definition file") + parser.add_argument("-o", required=True, type=str, help="Output .rdb file") + + args = parser.parse_args() + + # Load tag groups + tag_groups = load_tag_groups(args.g) + + # Load raw database file + segbits = load_segbits(args.i) + + # Mask out bits present in "IN_USE" tag(s) as they are common to other tags + for tag in segbits.keys(): + if tag.endswith("IN_USE"): + prefix = tag[:tag.index("IN_USE")] + tags_to_mask = [t for t in segbits.keys() if t.startswith(prefix)] + mask_out_bits(segbits, segbits[tag], tags_to_mask) + + tags_to_remove = set() + for tag, bits in segbits.items(): + if len(bits) == 0: + tags_to_remove.add(tag) + + if 'REBUF' in tag and 'ACTIVE' not in tag: + # FIXME: Removing REBUF pips for now. + tags_to_remove.add(tag) + + for tag in tags_to_remove: + del segbits[tag] + + for tag in segbits.keys(): + if tag.endswith("_ACTIVE") and 'FREQ_BB' in tag: + if 'FREQ_BB_REBUF' in tag: + m = re.search('FREQ_BB_REBUF([0-9])', tag) + else: + m = re.search('FREQ_BB_NS([0-9])', tag) + assert m is not None, tag + + prefix = '.CMT_R_LOWER_B_CLK_FREQ_BB{}'.format(m.group(1)) + tags_to_mask = [t for t in segbits.keys() if t.endswith(prefix)] + mask_out_bits(segbits, segbits[tag], tags_to_mask) + + # Find common bits + bit_groups = find_common_bits_for_tag_groups(segbits, tag_groups) + # Apply tag grouping + segbits = group_tags(segbits, tag_groups, bit_groups) + + # Save fixed database file + save_segbits(args.o, segbits) + + +if __name__ == "__main__": + main() diff --git a/fuzzers/034b-cmt-mmcm-pips/generate.py b/fuzzers/034b-cmt-mmcm-pips/generate.py new file mode 100644 index 00000000..548bc138 --- /dev/null +++ b/fuzzers/034b-cmt-mmcm-pips/generate.py @@ -0,0 +1,221 @@ +#!/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 + +from prjxray.segmaker import Segmaker +import os +import os.path + + +def bitfilter(frame, word): + if frame < 25 or frame > 29: + return False + + return True + + +def merge_lr_wires(wire): + wire = wire.replace('CMT_L_LOWER_B', 'CMT_LRMAP_LOWER_B') + wire = wire.replace('CMT_R_LOWER_B', 'CMT_LRMAP_LOWER_B') + return wire + + +def main(): + segmk = Segmaker("design.bits") + + designdata = {} + tiledata = {} + pipdata = {} + ppipdata = {} + ignpip = set() + all_clks = {} + + piplists = ['cmt_top_l_lower_b.txt', 'cmt_top_r_lower_b.txt'] + wirelists = ['cmt_top_l_lower_b_wires.txt', 'cmt_top_r_lower_b_wires.txt'] + ppiplists = ['ppips_cmt_top_l_lower_b.db', 'ppips_cmt_top_r_lower_b.db'] + + # Load PIP lists + print("Loading PIP lists...") + for piplist in piplists: + with open(os.path.join(os.getenv('FUZDIR'), '..', 'piplist', 'build', + 'cmt_top_lower', piplist)) as f: + for l in f: + pip, is_directional = l.strip().split(' ') + tile_type, dst, src = pip.split('.') + if tile_type not in pipdata: + pipdata[tile_type] = [] + all_clks[tile_type] = set() + + pipdata[tile_type].append((src, dst)) + if dst.split('_')[-1].startswith('CLK'): + all_clks[tile_type].add(src) + + if not int(is_directional): + pipdata[tile_type].append((dst, src)) + if src.split('_')[-1].startswith('CLK'): + all_clks[tile_type].add(dst) + + wiredata = {} + for wirelist in wirelists: + with open(os.path.join(os.getenv('FUZDIR'), '..', 'piplist', 'build', + 'cmt_top_lower', wirelist)) as f: + for l in f: + tile_type, wire = l.strip().split() + + if tile_type not in wiredata: + wiredata[tile_type] = set() + + wiredata[tile_type].add(wire) + + # Load PPIP lists (to exclude them) + print("Loading PPIP lists...") + for ppiplist in ppiplists: + fname = os.path.join( + os.getenv('FUZDIR'), '..', '071-ppips', 'build', ppiplist) + with open(fname, 'r') as f: + for l in f: + pip_data, pip_type = l.strip().split() + + if pip_type != 'always': + continue + + tile_type, dst, src = pip_data.split('.') + if tile_type not in ppipdata: + ppipdata[tile_type] = [] + + ppipdata[tile_type].append((src, dst)) + + # Load desgin data + print("Loading design data...") + with open("design.txt", "r") as f: + for line in f: + fields = line.strip().split(",") + designdata[fields[0]] = fields[1:] + + with open("design_pips.txt", "r") as f: + for line in f: + tile, pip, src, dst, pnum, pdir = line.split() + + if not tile.startswith('CMT_TOP'): + continue + + if 'UPPER_B' in tile: + continue + if 'LOWER_T' in tile: + continue + + pip_prefix, _ = pip.split(".") + tile_from_pip, tile_type = pip_prefix.split('/') + assert tile == tile_from_pip + _, src = src.split("/") + _, dst = dst.split("/") + pnum = int(pnum) + pdir = int(pdir) + + if tile not in tiledata: + tiledata[tile] = { + "type": tile_type, + "pips": set(), + "srcs": set(), + "dsts": set(), + } + + tiledata[tile]["pips"].add((src, dst)) + tiledata[tile]["srcs"].add(src) + tiledata[tile]["dsts"].add(dst) + + if pdir == 0: + tiledata[tile]["srcs"].add(dst) + tiledata[tile]["dsts"].add(src) + + #if dst.startswith('CMT_TOP_R_LOWER_B_CLK') or \ + # dst.startswith('CMT_TOP_L_LOWER_B_CLK'): + # ignpip.add((src, dst)) + + active_wires = {} + with open("design_wires.txt", "r") as f: + for l in f: + tile, wire = l.strip().split('/') + + if tile not in active_wires: + active_wires[tile] = set() + + active_wires[tile].add(wire) + + tags = {} + + # Populate IN_USE tags + for tile, (site, in_use) in designdata.items(): + if tile not in tags: + tags[tile] = {} + + tile_type = tile.rsplit("_", maxsplit=1)[0] + tags[tile]["IN_USE"] = int(in_use) + + # Populate PIPs + active_clks = {} + for tile in tags.keys(): + tile_type = tile.rsplit("_", maxsplit=1)[0] + + in_use = tags[tile]["IN_USE"] + + if not in_use: + active_pips = [] + else: + active_pips = tiledata[tile]["pips"] + + for src, dst in pipdata[tile_type]: + + if (src, dst) in ignpip: + continue + if (src, dst) in ppipdata[tile_type]: + continue + + tag = "{}.{}".format(merge_lr_wires(dst), merge_lr_wires(src)) + val = in_use if (src, dst) in active_pips else False + + if not (in_use and not val): + if tile not in active_clks: + active_clks[tile] = set() + + active_clks[tile].add(src) + tags[tile][tag] = int(val) + + for wire in wiredata[tile_type]: + if 'CLK' not in wire: + continue + + if 'CLKOUT' in wire: + continue + + if 'CLKFB' in wire: + continue + + if 'REBUF' in wire: + continue + + wire = merge_lr_wires(wire) + + if tile not in active_wires: + active_wires[tile] = set() + segmk.add_tile_tag( + tile, '{}_ACTIVE'.format(wire), wire in active_wires[tile]) + + # Output tags + for tile, tile_tags in tags.items(): + for t, v in tile_tags.items(): + segmk.add_tile_tag(tile, t, v) + + segmk.compile(bitfilter=bitfilter) + segmk.write() + + +if __name__ == "__main__": + main() diff --git a/fuzzers/034b-cmt-mmcm-pips/generate.tcl b/fuzzers/034b-cmt-mmcm-pips/generate.tcl new file mode 100644 index 00000000..2283ec1b --- /dev/null +++ b/fuzzers/034b-cmt-mmcm-pips/generate.tcl @@ -0,0 +1,192 @@ +# 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 +source "$::env(XRAY_DIR)/utils/utils.tcl" + +proc write_pip_txtdata {filename} { + puts "FUZ([pwd]): Writing $filename." + set fp [open $filename w] + set nets [get_nets -hierarchical] + set nnets [llength $nets] + set neti 0 + foreach net $nets { + incr neti + if {($neti % 100) == 0 } { + puts "FUZ([pwd]): Dumping pips from net $net ($neti / $nnets)" + } + foreach pip [get_pips -of_objects $net] { + set tile [get_tiles -of_objects $pip] + set src_wire [get_wires -uphill -of_objects $pip] + set dst_wire [get_wires -downhill -of_objects $pip] + set num_pips [llength [get_nodes -uphill -of_objects [get_nodes -of_objects $dst_wire]]] + set dir_prop [get_property IS_DIRECTIONAL $pip] + puts $fp "$tile $pip $src_wire $dst_wire $num_pips $dir_prop" + } + } + close $fp +} + +proc write_used_wires {filename} { + puts "FUZ([pwd]): Writing $filename." + + set fp [open $filename w] + set nets [get_nets -hierarchical] + set nnets [llength $nets] + set neti 0 + foreach net $nets { + foreach node [get_nodes -of $net] { + foreach wire [get_wires -of $node] { + puts $fp "$wire" + } + } + } + close $fp +} + +proc load_routes {filename} { + puts "MANROUTE: Loading routes from $filename" + + set fp [open $filename r] + foreach line [split [read $fp] "\n"] { + if {$line eq ""} { + continue + } + + puts "MANROUTE: Line: $line" + + # Parse the line + set fields [split $line " "] + set tile_name [lindex $fields 0] + set site_name [lindex $fields 1] + set pin_name [lindex $fields 2] + set route_dir [lindex $fields 3] + set wires [lrange $fields 4 end] + + # Get net + set tile [get_tiles $tile_name] + set site [get_sites -of_objects $tile $site_name] + set pin [get_site_pins -of_objects $site "*/$pin_name"] + set net [get_nets -quiet -of_objects $pin] + + if {$net eq "" } { + puts "MANROUTE: No net for pin $pin_name found! Skipping..." + continue + } + + # Fixed part read from file + set route_list {} + foreach wire $wires { + lappend route_list [get_nodes -of_objects [get_wires -of_objects $tile "*/$wire"]] + } + + # Complete the route + if {$route_dir eq "up"} { + set node_to [lindex $route_list 0] + set node_from [get_nodes -of_objects [get_site_pins -filter {DIRECTION == OUT} -of_objects $net]] + + set rpart [find_routing_path -from $node_from -to $node_to] + if {$rpart eq ""} { + puts "MANROUTE: No possible route continuation for net $net" + continue + } + + set route_list [concat [lrange $rpart 0 end-1] $route_list] + } + + if {$route_dir eq "down"} { + set node_from [lindex $route_list e] + set node_to [get_nodes -of_objects [get_site_pins -filter {DIRECTION == IN} -of_objects $net]] + + set rpart [find_routing_path -from $node_from -to $node_to] + if {$rpart eq ""} { + puts "MANROUTE: No possible route continuation for net $net" + continue + } + set route_list [concat $route_list [lrange $rpart 1 end]] + } + + # Set the fixed route + puts "MANROUTE: Net: $net, Route: $route_list. routing..." + regsub -all {{}} $route_list "" route + if [catch {set_property FIXED_ROUTE $route $net} ] { + puts "MANROUTE: Net $net failed to set FIXED_ROUTE, ripping up..." + set_property FIXED_ROUTE "" $net + set_property IS_ROUTE_FIXED 0 $net + route_design -unroute -nets $net + } + + # Route the single net. Needed to detect conflicts when evaluating + # other ones + puts "Running route design" + route_design -quiet -directive Quick -nets $net + puts "Done running route design" + + # Check for conflicts. + set status [get_property ROUTE_STATUS $net] + if {$status ne "ROUTED"} { + # Ripup and discard the fixed route. + set_property FIXED_ROUTE "" $net + route_design -unroute -nets $net + puts "MANROUTE: Net $net status $status, ripping up..." + } else { + set_property IS_ROUTE_FIXED 1 $net + puts "MANROUTE: Successful manual route for $net" + } + } + + close $fp +} + +proc run {} { + create_project -force -part $::env(XRAY_PART) design design + read_verilog top.v + synth_design -top top + + set_property CFGBVS VCCO [current_design] + set_property CONFIG_VOLTAGE 3.3 [current_design] + set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design] + + # Disable MMCM frequency etc sanity checks + set_property IS_ENABLED 0 [get_drc_checks {PDRC-29}] + set_property IS_ENABLED 0 [get_drc_checks {PDRC-30}] + set_property IS_ENABLED 0 [get_drc_checks {AVAL-50}] + set_property IS_ENABLED 0 [get_drc_checks {AVAL-53}] + set_property IS_ENABLED 0 [get_drc_checks {REQP-126}] + # PLL + set_property IS_ENABLED 0 [get_drc_checks {PDRC-43}] + set_property IS_ENABLED 0 [get_drc_checks {REQP-161}] + set_property IS_ENABLED 0 [get_drc_checks {AVAL-78}] + set_property IS_ENABLED 0 [get_drc_checks {AVAL-81}] + set_property IS_ENABLED 0 [get_drc_checks {PDRC-38}] + set_property IS_ENABLED 0 [get_drc_checks {REQP-13}] + + place_design + + load_routes routes.txt + write_checkpoint -force design_pre_route.dcp + + route_design -directive Quick -preserve + + if {[llength [get_nets -filter {ROUTE_STATUS!="ROUTED"}]] ne 0} { + set nets [get_nets -filter {IS_ROUTE_FIXED==1}] + puts "MANROUTE: Got unrouted nets: $nets" + puts "MANROUTE: Ripping up and starting again with no fixed routes" + + route_design -unroute + set_property FIXED_ROUTE "" $nets + set_property IS_ROUTE_FIXED 0 $nets + + route_design -directive Quick + } + + write_checkpoint -force design.dcp + write_bitstream -force design.bit + write_pip_txtdata design_pips.txt + write_used_wires design_wires.txt +} + +run diff --git a/fuzzers/034b-cmt-mmcm-pips/tag_groups.txt b/fuzzers/034b-cmt-mmcm-pips/tag_groups.txt new file mode 100644 index 00000000..e69de29b diff --git a/fuzzers/034b-cmt-mmcm-pips/top.py b/fuzzers/034b-cmt-mmcm-pips/top.py new file mode 100644 index 00000000..43b01dc5 --- /dev/null +++ b/fuzzers/034b-cmt-mmcm-pips/top.py @@ -0,0 +1,419 @@ +#!/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 os +import random +random.seed(int(os.getenv("SEED"), 16)) +from prjxray import util +from prjxray.db import Database + + +class PipList(object): + def __init__(self): + self.piplist = {} + self.ppiplist = {} + + def get_pip_and_ppip_list_for_tile_type(self, tile_type): + + # Load PIP list for the tile type if not already loaded + if tile_type not in self.piplist: + self.piplist[tile_type] = [] + + fname = os.path.join( + os.getenv('FUZDIR'), '..', 'piplist', 'build', 'cmt_top_lower', + tile_type.lower() + '.txt') + + with open(fname, "r") as f: + for l in f: + pip, is_directional = l.strip().split(' ') + tile, dst, src = pip.split('.') + if tile_type == tile: + self.piplist[tile_type].append( + (src, dst, bool(int(is_directional)))) + + # Load PPIP list for the tile type if not already loaded + if tile_type not in self.ppiplist: + self.ppiplist[tile_type] = [] + + fname = os.path.join( + os.getenv('FUZDIR'), '..', '071-ppips', 'build', + 'ppips_' + tile_type.lower() + '.db') + + with open(fname, "r") as f: + for l in f: + pip_data, pip_type = l.strip().split() + + if pip_type != 'always': + continue + + tile, dst, src = pip_data.strip().split('.') + if tile_type == tile: + self.ppiplist[tile_type].append((src, dst, True)) + + return self.piplist[tile_type], self.ppiplist[tile_type] + + +def find_phasers_for_mmcm(grid, loc): + gridinfo = grid.gridinfo_at_loc((loc[0], loc[1] - 9)) + + phasers = { + 'IN': [], + 'OUT': [], + } + + for site_name, site_type in gridinfo.sites.items(): + if site_type == 'PHASER_IN_PHY': + phasers['IN'].append(site_name) + elif site_type == 'PHASER_OUT_PHY': + phasers['OUT'].append(site_name) + + assert len(phasers['IN']) > 0 + assert len(phasers['OUT']) > 0 + + phasers['IN'].sort() + phasers['OUT'].sort() + + return phasers + + +def gen_sites(): + 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 ['MMCME2_ADV']: + phasers = find_phasers_for_mmcm(grid, loc) + yield tile_name, site_name, phasers + + +def get_random_route_from_site_pin( + pip_list, tile_name, site_pin, direction, occupied_wires): + + # A map of MMCM site pins to wires they are connected to. + pin_to_wire = { + "CMT_TOP_L_LOWER_B": { + "CLKIN1": "CMT_LR_LOWER_B_MMCM_CLKIN1", + "CLKIN2": "CMT_LR_LOWER_B_MMCM_CLKIN2", + "CLKFBIN": "CMT_LR_LOWER_B_MMCM_CLKFBIN", + "CLKOUT0": "CMT_LR_LOWER_B_MMCM_CLKOUT0", + "CLKOUT1": "CMT_LR_LOWER_B_MMCM_CLKOUT1", + "CLKOUT2": "CMT_LR_LOWER_B_MMCM_CLKOUT2", + "CLKOUT3": "CMT_LR_LOWER_B_MMCM_CLKOUT3", + "CLKOUT4": "CMT_LR_LOWER_B_MMCM_CLKOUT4", + "CLKOUT5": "CMT_LR_LOWER_B_MMCM_CLKOUT5", + "CLKOUT6": "CMT_LR_LOWER_B_MMCM_CLKOUT6", + }, + "CMT_TOP_R_LOWER_B": { + "CLKIN1": "CMT_LR_LOWER_B_MMCM_CLKIN1", + "CLKIN2": "CMT_LR_LOWER_B_MMCM_CLKIN2", + "CLKFBIN": "CMT_LR_LOWER_B_MMCM_CLKFBIN", + "CLKOUT0": "CMT_LR_LOWER_B_MMCM_CLKOUT0", + "CLKOUT1": "CMT_LR_LOWER_B_MMCM_CLKOUT1", + "CLKOUT2": "CMT_LR_LOWER_B_MMCM_CLKOUT2", + "CLKOUT3": "CMT_LR_LOWER_B_MMCM_CLKOUT3", + "CLKOUT4": "CMT_LR_LOWER_B_MMCM_CLKOUT4", + "CLKOUT5": "CMT_LR_LOWER_B_MMCM_CLKOUT5", + "CLKOUT6": "CMT_LR_LOWER_B_MMCM_CLKOUT6", + }, + } + + # Get tile type + tile_type = tile_name.rsplit("_", maxsplit=1)[0] + + # Get all PIPs (PIPs + PPIPs) + pips, ppips = pip_list.get_pip_and_ppip_list_for_tile_type(tile_type) + all_pips = pips + ppips + + # The first wire + wire = pin_to_wire[tile_type][site_pin] + + # Walk randomly. + route = [] + while True: + route.append(wire) + + wires = [] + + for src, dst, is_directional in all_pips: + if direction == "down" and src == wire: + next_wire = dst + elif direction == "down" and dst == wire and not is_directional: + next_wire = src + elif direction == "up" and dst == wire: + next_wire = src + elif direction == "up" and src == wire and not is_directional: + next_wire = dst + else: + continue + + if next_wire not in occupied_wires: + wires.append(next_wire) + + if len(wires) == 0: + break + + wire = random.choice(wires) + occupied_wires.add(wire) + + # For "up" direction reverse the route. + if direction == "down": + return route + if direction == "up": + return route[::-1] + + +def main(): + + # 8 inputs per clock region + # 5 clock regions for device + max_clk_inputs = 8 * 5 + clkin_idx = 0 + + print( + ''' +module top( + input wire [{nclkin}:0] clkin +); + + (* KEEP, DONT_TOUCH *) + LUT6 dummy(); + '''.format(nclkin=max_clk_inputs - 1)) + + pip_list = PipList() + bufg_count = 0 + + design_file = open('design.txt', 'w') + routes_file = open('routes.txt', 'w') + + count = 0 + mmcms = sorted(gen_sites(), key=lambda x: x[0]) + random.shuffle(mmcms) + for tile, site, phasers in mmcms: + in_use = random.randint(0, 2) > 0 + count += 1 + + design_file.write("{},{},{}\n".format(tile, site, int(in_use))) + + if not in_use: + continue + + # Generate random routes to/from some pins + routes = {} + endpoints = {} + + pins = [ + ('CLKIN1', 'up'), + ('CLKIN2', 'up'), + ('CLKFBIN', 'up'), + + # Sometimes manually randomized route for CLKOUTx conflicts with + # the verilog design. + ('CLKOUT0', 'down'), + ('CLKOUT1', 'down'), + ('CLKOUT2', 'down'), + ('CLKOUT3', 'down'), + ('CLKOUT4', 'down'), + ('CLKOUT5', 'down'), + ('CLKOUT6', 'down'), + ] + + occupied_wires = set() + for pin, dir in pins: + + route = get_random_route_from_site_pin( + pip_list, tile, pin, dir, occupied_wires) + if route is None: + endpoints[pin] = "" + continue + + routes[pin] = ( + route, + dir, + ) + endpoints[pin] = route[-1] if dir == 'down' else route[0] + + internal_feedback = endpoints['CLKFBIN'].endswith('CLKFBOUT') + + # Store them in a random order so the TCL script will try to route + # them also in the random order. + lines = [] + for pin, ( + route, + dir, + ) in routes.items(): + + route_str = " ".join(route) + lines.append( + '{} {} {} {} {}\n'.format(tile, site, pin, dir, route_str)) + + random.shuffle(lines) + routes_file.writelines(lines) + + clkfbin_src = random.choice(('BUFH', 'logic')) + + if internal_feedback: + COMPENSATION = "INTERNAL" + else: + if clkfbin_src == 'logic': + COMPENSATION = 'EXTERNAL' + else: + COMPENSATION = "ZHOLD" + + print( + """ + wire clkfbin_{site}; + wire clkin1_{site}; + wire clkin2_{site}; + wire clkfbout_mult_{site}; + wire clkout0_{site}; + wire clkout1_{site}; + wire clkout2_{site}; + wire clkout3_{site}; + wire clkout4_{site}; + wire clkout5_{site}; + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + MMCME2_ADV #( + .COMPENSATION("{COMPENSATION}") + ) mmcm_{site} ( + .CLKFBOUT(clkfbout_mult_{site}), + .CLKOUT0(clkout0_{site}), + .CLKOUT1(clkout1_{site}), + .CLKOUT2(clkout2_{site}), + .CLKOUT3(clkout3_{site}), + .CLKOUT4(clkout4_{site}), + .CLKOUT5(clkout5_{site}), + .DRDY(), + .LOCKED(), + .DO(), + .CLKFBIN(clkfbin_{site}), + .CLKIN1(clkin1_{site}), + .CLKIN2(clkin2_{site}), + .CLKINSEL(), + .DCLK(), + .DEN(), + .DWE(), + .PWRDWN(), + .RST(), + .DI(), + .DADDR()); + """.format(site=site, COMPENSATION=COMPENSATION)) + + for clkout in range(4, 6): + # CLKOUT4 and CLKOUT5 can only drive one signal type + if random.randint(0, 1) and bufg_count < 16: + bufg_count += 1 + print( + """ + (* KEEP, DONT_TOUCH *) + BUFG ( + .I(clkout{idx}_{site}) + );""".format(idx=clkout, site=site)) + + any_phaser = False + + for clkout in range(4): + # CLKOUT0-CLKOUT3 can drive: + # - Global drivers (e.g. BUFG) + # - PHASER_[IN|OUT]_[CA|DB]_FREQREFCLK via BB_[0-3] + drive_bufg = random.randint(0, 1) and bufg_count < 16 + drive_phaser = 0 #random.randint(0, 1) + + if drive_bufg: + bufg_count += 1 + print( + """ + (* KEEP, DONT_TOUCH *) + BUFG ( + .I(clkout{idx}_{site}) + );""".format(idx=clkout, site=site)) + + if drive_phaser and not any_phaser and False: + any_phaser = True + print( + """ + (* KEEP, DONT_TOUCH, LOC="{phaser_loc}" *) + PHASER_OUT phaser_{site}( + .FREQREFCLK(clkout{idx}_{site}) + );""".format(idx=clkout, site=site, phaser_loc=phasers['OUT'][0])) + + if internal_feedback: + print( + """ + assign clkfbin_{site} = clkfbout_mult_{site}; + """.format(site=site)) + else: + if clkfbin_src == 'BUFH': + print( + """ + (* KEEP, DONT_TOUCH *) + BUFH ( + .I(clkfbout_mult_{site}), + .O(clkfbin_{site}) + );""".format(site=site)) + elif clkfbin_src == 'logic': + print( + """ + (* KEEP, DONT_TOUCH *) + LUT6 # (.INIT(64'h5555555555555555)) + clkfbin_logic_{site} ( + .I0(clkfbout_mult_{site}), + .O(clkfbin_{site}) + ); + """.format(site=site)) + else: + assert False, clkfbin_src + + for clkin in range(2): + clkin_src = random.choice(( + 'BUFH', + 'BUFR', + 'logic', + )) + + if clkin_src == 'BUFH': + print( + """ + (* KEEP, DONT_TOUCH *) + BUFH ( + .O(clkin{idx}_{site}), + .I(clkin{idx2}) + );""".format(idx=clkin + 1, idx2=clkin_idx, site=site)) + elif clkin_src == 'BUFR': + print( + """ + (* KEEP, DONT_TOUCH *) + BUFR ( + .O(clkin{idx}_{site}), + .I(clkin{idx2}) + );""".format(idx=clkin + 1, idx2=clkin_idx, site=site)) + elif clkin_src == 'logic': + print( + """ + (* KEEP, DONT_TOUCH *) + LUT6 # (.INIT(64'h5555555555555555)) + clkin{idx}_logic_{site} ( + .I0(clkin{idx2}), + .O(clkin{idx}_{site}) + ); + """.format(idx=clkin + 1, idx2=clkin_idx, site=site)) + else: + assert False, (clkin, clkin_src) + + clkin_idx += 1 + + print("endmodule") + + +if __name__ == '__main__': + main() diff --git a/fuzzers/Makefile b/fuzzers/Makefile index 794e430d..7c3fe0d3 100644 --- a/fuzzers/Makefile +++ b/fuzzers/Makefile @@ -113,8 +113,10 @@ $(eval $(call fuzzer,027-bram36-config,005-tilegrid,all)) $(eval $(call fuzzer,028-fifo-config,005-tilegrid,all)) $(eval $(call fuzzer,029-bram-fifo-config,005-tilegrid,all)) $(eval $(call fuzzer,030-iob,005-tilegrid,all)) +$(eval $(call fuzzer,031-cmt-mmcm,005-tilegrid,all)) $(eval $(call fuzzer,032-cmt-pll,005-tilegrid,all)) $(eval $(call fuzzer,034-cmt-pll-pips,005-tilegrid 071-ppips,all)) +$(eval $(call fuzzer,034b-cmt-mmcm-pips,005-tilegrid 071-ppips,all)) $(eval $(call fuzzer,035-iob-ilogic,005-tilegrid,all)) $(eval $(call fuzzer,035a-iob-idelay,005-tilegrid,all)) $(eval $(call fuzzer,035b-iob-iserdes,005-tilegrid,all)) diff --git a/fuzzers/int_maketodo.py b/fuzzers/int_maketodo.py index 754eb24b..5634bcee 100644 --- a/fuzzers/int_maketodo.py +++ b/fuzzers/int_maketodo.py @@ -32,7 +32,7 @@ def load_pipfile(pipfile, verbose=False): with open(pipfile, "r") as f: # INT_L.WW2BEG0.SR1BEG_S0 for line in f: - tag = line.strip() + tag = line.strip().split(' ')[0] prefix_line = getprefix(tag) if tile_type is None: tile_type = prefix_line diff --git a/prjxray/segmaker.py b/prjxray/segmaker.py index 15f99342..82987c05 100644 --- a/prjxray/segmaker.py +++ b/prjxray/segmaker.py @@ -371,6 +371,8 @@ class Segmaker: tile_type_norm = 'IOI3' if tile_type_norm in ['LIOI3_TBYTETERM', 'RIOI3_TBYTETERM']: tile_type_norm = 'IOI3' + if tile_type_norm in ['CMT_TOP_L_LOWER_B', 'CMT_TOP_R_LOWER_B']: + tile_type_norm = 'CMT_LOWER_B' # ignore dummy tiles (ex: VBRK) if len(tiledata['bits']) == 0: diff --git a/utils/mergedb.sh b/utils/mergedb.sh index 36032d97..2622434b 100755 --- a/utils/mergedb.sh +++ b/utils/mergedb.sh @@ -145,6 +145,12 @@ case "$1" in cmt_top_l_upper_t) sed < "$2" > "$tmp1" -e 's/^CMT_UPPER_T\./CMT_TOP_L_UPPER_T./' ;; + cmt_top_r_lower_b) + sed < "$2" > "$tmp1" -e 's/^CMT_LOWER_B\./CMT_TOP_R_LOWER_B./' ;; + + cmt_top_l_lower_b) + sed < "$2" > "$tmp1" -e 's/^CMT_LOWER_B\./CMT_TOP_L_LOWER_B./' ;; + cfg_center_mid) cp "$2" "$tmp1" ;;