mirror of https://github.com/openXC7/prjxray.git
Merge pull request #1447 from litghost/add_mmcm_pips
Add initial MMCM feature and PIP support.
This commit is contained in:
commit
34043d1b3d
|
|
@ -16,7 +16,7 @@ action {
|
|||
# Whole directories
|
||||
# regex: "**/build/**" - Currently kokoro dies on number of artifacts.
|
||||
regex: "**/build/*.log"
|
||||
regex: "**/logs/**"
|
||||
regex: "**/logs_*/**"
|
||||
# The database
|
||||
regex: "**/database/artix7/**"
|
||||
strip_prefix: "github/symbiflow-prjxray-continuous-db-artix7/"
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ action {
|
|||
# Whole directories
|
||||
# regex: "**/build/**" - Currently kokoro dies on number of artifacts.
|
||||
regex: "**/build/*.log"
|
||||
regex: "**/logs/**"
|
||||
regex: "**/logs_*/**"
|
||||
# The database
|
||||
regex: "**/database/kintex7/**"
|
||||
strip_prefix: "github/symbiflow-prjxray-continuous-db-kintex7/"
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ action {
|
|||
# Whole directories
|
||||
# regex: "**/build/**" - Currently kokoro dies on number of artifacts.
|
||||
regex: "**/build/*.log"
|
||||
regex: "**/logs/**"
|
||||
regex: "**/logs_*/**"
|
||||
# The database
|
||||
regex: "**/database/zynq7/**"
|
||||
strip_prefix: "github/symbiflow-prjxray-continuous-db-zynq7/"
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ action {
|
|||
# Whole directories
|
||||
# regex: "**/build/**" - Currently kokoro dies on number of artifacts.
|
||||
regex: "**/build/*.log"
|
||||
regex: "**/logs/**"
|
||||
regex: "**/logs_*/**"
|
||||
# The database
|
||||
regex: "**/database/%(part)s/**"
|
||||
strip_prefix: "github/symbiflow-prjxray-%(kokoro_type)s-db-%(part)s/"
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ action {
|
|||
# Whole directories
|
||||
# regex: "**/build/**" - Currently kokoro dies on number of artifacts.
|
||||
regex: "**/build/*.log"
|
||||
regex: "**/logs/**"
|
||||
regex: "**/logs_*/**"
|
||||
# The database
|
||||
regex: "**/database/artix7/**"
|
||||
strip_prefix: "github/symbiflow-prjxray-presubmit-db-artix7/"
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ action {
|
|||
# Whole directories
|
||||
# regex: "**/build/**" - Currently kokoro dies on number of artifacts.
|
||||
regex: "**/build/*.log"
|
||||
regex: "**/logs/**"
|
||||
regex: "**/logs_*/**"
|
||||
# The database
|
||||
regex: "**/database/kintex7/**"
|
||||
strip_prefix: "github/symbiflow-prjxray-presubmit-db-kintex7/"
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ action {
|
|||
# Whole directories
|
||||
# regex: "**/build/**" - Currently kokoro dies on number of artifacts.
|
||||
regex: "**/build/*.log"
|
||||
regex: "**/logs/**"
|
||||
regex: "**/logs_*/**"
|
||||
# The database
|
||||
regex: "**/database/zynq7/**"
|
||||
strip_prefix: "github/symbiflow-prjxray-presubmit-db-zynq7/"
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 == MMCME2_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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
@ -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).
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,251 @@
|
|||
#!/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 '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:
|
||||
for idx in range(4):
|
||||
prefix1 = '.CMT_L_LOWER_B_CLK_FREQ_BB{}'.format(idx)
|
||||
prefix2 = '.CMT_R_LOWER_B_CLK_FREQ_BB{}'.format(idx)
|
||||
tags_to_mask = [
|
||||
t for t in segbits.keys()
|
||||
if t.endswith(prefix1) or t.endswith(prefix2)
|
||||
]
|
||||
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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
# 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_route {line} {
|
||||
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..."
|
||||
return
|
||||
}
|
||||
|
||||
set source_pin [get_pins -of $net -filter "DIRECTION == OUT"]
|
||||
if { [llength $source_pin] == 0 } {
|
||||
error "Failed to find source pin of net $net"
|
||||
}
|
||||
|
||||
set source_site_pins [get_site_pins -of $source_pin]
|
||||
puts "MANROUTE: Possible site pins for net $net: $source_site_pins"
|
||||
|
||||
# Fixed part read from file
|
||||
set route_list {}
|
||||
if { [string first / $wires] != -1 } {
|
||||
# Route list is just intermediate node and final node, fill it in
|
||||
if { [llength $wires] != 2} {
|
||||
error "Expected only 2 wires, found [llength $wires]"
|
||||
}
|
||||
|
||||
set intermediate_node [get_nodes [lindex $wires 0]]
|
||||
set dest_node [get_nodes -of_objects [get_wires -of_objects $tile "*/[lindex $wires 1]"]]
|
||||
if [catch {find_routing_path -from $intermediate_node -to $dest_node} route_list] {
|
||||
puts "MANROUTE: Failed to find routing path from $intermediate_node to $dest_node for net $net\nError: $route_list"
|
||||
return
|
||||
}
|
||||
|
||||
if { [llength $route_list] == 0 } {
|
||||
puts "MANROUTE: Failed to find routing path from $intermediate_node to $dest_node for net $net"
|
||||
return
|
||||
}
|
||||
} else {
|
||||
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"
|
||||
return
|
||||
}
|
||||
|
||||
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"
|
||||
return
|
||||
}
|
||||
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} result] {
|
||||
puts "MANROUTE: Net $net failed to set FIXED_ROUTE, ripping up...\nError: $result"
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
load_route $line
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_L_LOWER_B_CLK_FREQ_BB0 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_L_LOWER_B_CLK_FREQ_BB1 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_L_LOWER_B_CLK_FREQ_BB2 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_L_LOWER_B_CLK_FREQ_BB3 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_L_LOWER_B_CLK_IN3_HCLK CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_L_LOWER_B_CLK_IN3_INT
|
||||
|
||||
CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_L_LOWER_B_CLK_FREQ_BB0 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_L_LOWER_B_CLK_FREQ_BB1 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_L_LOWER_B_CLK_FREQ_BB2 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_L_LOWER_B_CLK_FREQ_BB3 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_L_LOWER_B_CLK_IN1_HCLK CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_L_LOWER_B_CLK_IN1_INT
|
||||
|
||||
CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_L_LOWER_B_CLK_FREQ_BB0 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_L_LOWER_B_CLK_FREQ_BB1 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_L_LOWER_B_CLK_FREQ_BB2 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_L_LOWER_B_CLK_FREQ_BB3 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_L_LOWER_B_CLK_IN2_HCLK CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_L_LOWER_B_CLK_IN2_INT
|
||||
|
||||
CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_R_LOWER_B_CLK_FREQ_BB0 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_R_LOWER_B_CLK_FREQ_BB1 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_R_LOWER_B_CLK_FREQ_BB2 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_R_LOWER_B_CLK_FREQ_BB3 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_R_LOWER_B_CLK_IN3_HCLK CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKFBIN.CMT_R_LOWER_B_CLK_IN3_INT
|
||||
|
||||
CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_R_LOWER_B_CLK_FREQ_BB0 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_R_LOWER_B_CLK_FREQ_BB1 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_R_LOWER_B_CLK_FREQ_BB2 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_R_LOWER_B_CLK_FREQ_BB3 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_R_LOWER_B_CLK_IN1_HCLK CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN1.CMT_R_LOWER_B_CLK_IN1_INT
|
||||
|
||||
CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_R_LOWER_B_CLK_FREQ_BB0 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_R_LOWER_B_CLK_FREQ_BB1 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_R_LOWER_B_CLK_FREQ_BB2 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_R_LOWER_B_CLK_FREQ_BB3 CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_R_LOWER_B_CLK_IN2_HCLK CMT_LOWER_B.CMT_LR_LOWER_B_MMCM_CLKIN2.CMT_R_LOWER_B_CLK_IN2_INT
|
||||
|
|
@ -0,0 +1,436 @@
|
|||
#!/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 find_hclk_ref_wires_for_mmcm(grid, loc):
|
||||
tilename = grid.tilename_at_loc((loc[0], loc[1] - 17))
|
||||
gridinfo = grid.gridinfo_at_tilename(tilename)
|
||||
|
||||
assert gridinfo.tile_type in ['HCLK_CMT_L', 'HCLK_CMT']
|
||||
|
||||
# HCLK_CMT_MUX_OUT_FREQ_REF[0-3]
|
||||
wires = []
|
||||
for idx in range(4):
|
||||
wires.append('{}/HCLK_CMT_MUX_OUT_FREQ_REF{}'.format(tilename, idx))
|
||||
|
||||
return wires
|
||||
|
||||
|
||||
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)
|
||||
hclk_wires = find_hclk_ref_wires_for_mmcm(grid, loc)
|
||||
yield tile_name, site_name, phasers, hclk_wires
|
||||
|
||||
|
||||
def get_random_route_from_site_pin(
|
||||
pip_list, tile_name, site_pin, direction, occupied_wires, hclk_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]
|
||||
|
||||
if site_pin in ["CLKIN1", "CLKIN2", "CLKFBIN"] and random.random() < .2:
|
||||
return [random.choice(hclk_wires), wire]
|
||||
|
||||
# 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, hclk_wires 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, hclk_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 = []
|
||||
pins = sorted(routes.keys())
|
||||
random.shuffle(pins)
|
||||
for pin in pins:
|
||||
(route, dir) = routes[pin]
|
||||
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()
|
||||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -645,11 +645,11 @@ def run_fuzzer(fuzzer_name, fuzzer_dir, fuzzer_logdir, logger, will_retry):
|
|||
--------------------------------------------------------------------------
|
||||
!Failed! @ {time_end} with exit code: {retcode}
|
||||
--------------------------------------------------------------------------
|
||||
- STDOUT: -
|
||||
- STDOUT (see {stdout_fname} for full log):
|
||||
--------------------------------------------------------------------------
|
||||
{stdout_log}
|
||||
--------------------------------------------------------------------------
|
||||
- STDERR: -
|
||||
- STDERR (see {stderr_fname} for full log):
|
||||
--------------------------------------------------------------------------
|
||||
{stderr_log}
|
||||
--------------------------------------------------------------------------
|
||||
|
|
@ -657,8 +657,10 @@ def run_fuzzer(fuzzer_name, fuzzer_dir, fuzzer_logdir, logger, will_retry):
|
|||
--------------------------------------------------------------------------
|
||||
""",
|
||||
retcode=retcode,
|
||||
stdout_log=open(fuzzer_stdout).read(),
|
||||
stderr_log=open(fuzzer_stderr).read(),
|
||||
stdout_fname=fuzzer_stdout,
|
||||
stdout_log='\n'.join(last_lines(open(fuzzer_stdout), 1000)),
|
||||
stderr_fname=fuzzer_stderr,
|
||||
stderr_log='\n'.join(last_lines(open(fuzzer_stderr), 1000)),
|
||||
time_end=time_end.isoformat())
|
||||
else:
|
||||
# Log the last 100 lines of a successful run
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ def read_segbits(f):
|
|||
|
||||
parts = l.split(' ')
|
||||
|
||||
assert len(parts) > 1
|
||||
assert len(parts) > 1, l
|
||||
|
||||
segbits[parts[0]] = [parsebit(val) for val in parts[1:]]
|
||||
|
||||
|
|
|
|||
|
|
@ -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" ;;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue