Add initial MMCM feature and PIP support.

Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
This commit is contained in:
Keith Rothman 2020-10-02 14:44:41 -07:00
parent ac8d30e3fe
commit 90d420eef3
21 changed files with 2080 additions and 230 deletions

View File

@ -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 \

View File

@ -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

View File

@ -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),

View File

@ -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

View File

@ -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.

View File

@ -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()

View File

@ -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

View File

@ -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
verilog.top_harness(DIN_N, DOUT_N)
def main():
sites = sorted(list(gen_sites()))
max_sites = len(sites)
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))
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
// ---------------------------------------------------------------------
''')
routes_file = open('routes.txt', 'w')
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";
"""
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()

View File

@ -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()

View File

@ -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

View File

@ -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).

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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))

View File

@ -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

View File

@ -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:

View File

@ -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" ;;