Refactor PLL segbits to leverage known register file.

Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
This commit is contained in:
Keith Rothman 2019-06-26 12:39:50 -07:00
parent b8f64484da
commit 68ad409d23
9 changed files with 479 additions and 101 deletions

View File

@ -1,12 +1,26 @@
# read/write width is relatively slow to resolve
# Even slower with multi bit masks...
N := 8
N := 50
include ../fuzzer.mk
database: $(SPECIMENS_OK)
database: build/segbits_cmt_top_upper_t.db
pushdb:
echo "FIXME" && false
build/segbits_cmt_top_upper_t.rdb: $(SPECIMENS_OK)
${XRAY_SEGMATCH} -o build/segbits_cmt_top_upper_t.rdb \
$(addsuffix /segdata_cmt_top_r_upper_t.txt,$(SPECIMENS)) \
$(addsuffix /segdata_cmt_top_l_upper_t.txt,$(SPECIMENS))
build/segbits_cmt_top_upper_t.db: build/segbits_cmt_top_upper_t.rdb write_pll_reg.py
python3 write_pll_reg.py \
--seg_in build/segbits_cmt_top_upper_t.rdb \
> build/segbits_cmt_top_upper_t.rdb2
${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf \
--seg-fn-in build/segbits_cmt_top_upper_t.rdb2 \
--seg-fn-out build/segbits_cmt_top_upper_t.db
pushdb: database
${XRAY_MERGEDB} cmt_top_r_upper_t build/segbits_cmt_top_upper_t.db
${XRAY_MERGEDB} cmt_top_l_upper_t build/segbits_cmt_top_upper_t.db
.PHONY: database pushdb

View File

@ -1,3 +1,4 @@
# Clock Management Tile (CMT) - Phase Lock Loop (PLL) Fuzzer
FIXME: Add description.
Bits that are part of the dynamic configration register interface (see APPNOTE
XAPP888) are handled specially.

View File

@ -7,14 +7,59 @@ from prjxray import verilog
def bus_tags(segmk, ps, site):
for param, tagname in [('CLKOUT0_DIVIDE', 'ZCLKOUT0_DIVIDE')]:
for reg, invert in [
('RST', 1),
('PWRDWN', 1),
('CLKINSEL', 0),
]:
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 ['ZHOLD', 'BUF_IN', 'EXTERNAL', 'INTERNAL']:
segmk.add_site_tag(
site, 'COMPENSATION.' + opt,
verilog.unquote(ps['COMPENSATION']) == opt)
for param in ['CLKFBOUT_MULT']:
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', 7),
('CLKOUT1_DIVIDE', 7),
('CLKOUT2_DIVIDE', 7),
('CLKOUT3_DIVIDE', 7),
('CLKOUT4_DIVIDE', 7),
('CLKOUT5_DIVIDE', 7),
('DIVCLK_DIVIDE', 6),
]:
# 1-128 => 0-127 for actual 7 bit value
paramadj = int(ps[param]) - 1
bitstr = [int(x) for x in "{0:07b}".format(paramadj)[::-1]]
# FIXME: only bits 0 and 1 resolving
# for i in range(7):
for i in range(2):
segmk.add_site_tag(site, '%s[%u]' % (param, i), 1 ^ bitstr[i])
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():
@ -26,11 +71,7 @@ def run():
f.readline()
for l in f:
j = json.loads(l)
ps = j['params']
assert j['module'] == 'my_PLLE2_ADV'
site = verilog.unquote(ps['LOC'])
bus_tags(segmk, ps, site)
bus_tags(segmk, j, j['site'])
segmk.compile()
segmk.write()

View File

@ -3,19 +3,14 @@ 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]
create_clock -period 10.00 [get_ports clk]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_IBUF]
# Disable MMCM frequency etc sanity checks
set_property IS_ENABLED 0 [get_drc_checks {PDRC-29}]
@ -24,10 +19,29 @@ 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}]
place_design
route_design
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

@ -3,102 +3,158 @@ 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
def gen_sites():
for _tile_name, site_name, _site_type in sorted(util.get_roi().gen_sites(
["PLLE2_ADV"])):
yield site_name
db = Database(util.get_db_root())
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 ['PLLE2_ADV']:
yield site_name
sites = list(gen_sites())
DUTN = len(sites)
DIN_N = DUTN * 8
DOUT_N = DUTN * 8
def main():
f = open('params.jl', 'w')
f.write('module,loc,params\n')
verilog.top_harness(DIN_N, DOUT_N)
print('module top(input clk);')
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 site in sorted(gen_sites()):
params = {
"site":
site,
"IS_RST_INVERTED":
random.randint(0, 1),
"IS_PWRDWN_INVERTED":
random.randint(0, 1),
"IS_CLKINSEL_INVERTED":
random.randint(0, 1),
"CLKFBOUT_MULT":
random.randint(2, 4),
"CLKOUT0_DIVIDE":
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),
"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',
))),
}
f.write('%s\n' % (json.dumps(params)))
for loci, site in enumerate(sites):
print(
"""
ports = {
'clk': 'clk',
'din': 'din[ %d +: 8]' % (8 * loci, ),
'dout': 'dout[ %d +: 8]' % (8 * loci, ),
}
params = {
"CLKOUT0_DIVIDE": random.randint(1, 128),
}
modname = "my_PLLE2_ADV"
verilog.instance(modname, "inst_%u" % loci, ports, params=params)
# LOC isn't support
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_PLLE2_ADV (input clk, input [7:0] din, output [7:0] dout);
parameter CLKOUT0_DIVIDE = 1;
parameter CLKOUT1_DIVIDE = 1;
parameter CLKOUT2_DIVIDE = 1;
parameter CLKOUT3_DIVIDE = 1;
parameter CLKOUT4_DIVIDE = 1;
parameter CLKOUT5_DIVIDE = 1;
parameter DIVCLK_DIVIDE = 1;
parameter CLKFBOUT_MULT = 5;
(* KEEP, DONT_TOUCH *)
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}" *)
PLLE2_ADV #(
.CLKOUT0_DIVIDE(CLKOUT0_DIVIDE),
.CLKOUT1_DIVIDE(CLKOUT1_DIVIDE),
.CLKOUT2_DIVIDE(CLKOUT2_DIVIDE),
.CLKOUT3_DIVIDE(CLKOUT3_DIVIDE),
.CLKOUT4_DIVIDE(CLKOUT4_DIVIDE),
.CLKOUT5_DIVIDE(CLKOUT5_DIVIDE),
.DIVCLK_DIVIDE(DIVCLK_DIVIDE),
.CLKFBOUT_MULT(CLKFBOUT_MULT)
) dut(
.CLKFBOUT(),
.CLKOUT0(dout[0]),
.CLKOUT1(),
.CLKOUT2(),
.CLKOUT3(),
.CLKOUT4(),
.CLKOUT5(),
.IS_RST_INVERTED({IS_RST_INVERTED}),
.IS_PWRDWN_INVERTED({IS_PWRDWN_INVERTED}),
.IS_CLKINSEL_INVERTED({IS_CLKINSEL_INVERTED}),
.CLKOUT0_DIVIDE({CLKOUT0_DIVIDE}),
.CLKOUT1_DIVIDE({CLKOUT1_DIVIDE}),
.CLKOUT2_DIVIDE({CLKOUT2_DIVIDE}),
.CLKOUT3_DIVIDE({CLKOUT3_DIVIDE}),
.CLKOUT4_DIVIDE({CLKOUT4_DIVIDE}),
.CLKOUT5_DIVIDE({CLKOUT5_DIVIDE}),
.CLKFBOUT_MULT({CLKFBOUT_MULT}),
.DIVCLK_DIVIDE({DIVCLK_DIVIDE}),
.STARTUP_WAIT({STARTUP_WAIT}),
.CLKOUT0_DUTY_CYCLE({CLKOUT0_DUTY_CYCLE}),
.COMPENSATION({COMPENSATION})
) pll_{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(),
.CLKIN1(),
.CLKIN1(clk),
.CLKIN2(),
.CLKINSEL(),
.DCLK(),
.DEN(),
.DWE(),
.PWRDWN(),
.RST(din[0]),
.RST(),
.DI(),
.DADDR());
endmodule
''')
(* KEEP, DONT_TOUCH *)
FDRE reg_clkfbout_mult_{site} (
.C(clkfbout_mult_{site})
);
(* KEEP, DONT_TOUCH *)
FDRE reg_clkout0_{site} (
.C(clkout0_{site})
);
(* KEEP, DONT_TOUCH *)
FDRE reg_clkout1_{site} (
.C(clkout1_{site})
);
(* KEEP, DONT_TOUCH *)
FDRE reg_clkout2_{site} (
.C(clkout2_{site})
);
(* KEEP, DONT_TOUCH *)
FDRE reg_clkout3_{site} (
.C(clkout3_{site})
);
(* KEEP, DONT_TOUCH *)
FDRE reg_clkout4_{site} (
.C(clkout4_{site})
);
(* KEEP, DONT_TOUCH *)
FDRE reg_clkout5_{site} (
.C(clkout5_{site})
);
""".format(**params))
print('endmodule')
f.close()
if __name__ == "__main__":
main()

View File

@ -0,0 +1,243 @@
import argparse
REGISTER_LAYOUT = {
'CLKOUT1': [
('LOW_TIME', 6),
('HIGH_TIME', 6),
('RESERVED', 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),
],
'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),
]
}
REGISTER_MAP = []
# 0x06 - 0x15
for output in ['CLKOUT5', 'CLKOUT0', 'CLKOUT1', 'CLKOUT2', 'CLKOUT3',
'CLKOUT4', None, 'CLKFBOUT']:
if output is not None:
REGISTER_MAP.append(('CLKOUT1', output))
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'))
class RegisterAddress(object):
def __init__(self, frame_offsets, bit_offset):
self.frame_index = 0
self.frame_offsets = frame_offsets
self.bit_offset = bit_offset
def next_bit(self):
output = '{}_{}'.format(
self.frame_offsets[self.frame_index], self.bit_offset)
self.frame_index += 1
if self.frame_index >= len(self.frame_offsets):
self.frame_index = 0
self.bit_offset += 1
return output
def passthrough_non_register_segbits(seg_in):
""" Filter input segbits file and capture register base offset.
Some PLL 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_UPPER_T.PLLE2.CLKOUT5_DIVIDE[1]'
bit_offset = 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 == 28
assert bit_index > 3
bit_offset = bit_index - 3
continue
parts = l.split()
feature_parts = parts[0].split('.')
if len(feature_parts) < 3:
print(l.strip())
continue
if '[' not in feature_parts[2]:
print(l.strip())
continue
base_feature = feature_parts[2].split('[')
if base_feature[0] in [
'CLKOUT0_DIVIDE',
'CLKOUT1_DIVIDE',
'CLKOUT2_DIVIDE',
'CLKOUT3_DIVIDE',
'CLKOUT4_DIVIDE',
'CLKOUT5_DIVIDE',
'DIVCLK_DIVIDE',
'CLKFBOUT_MULT',
'CLKOUT0_DUTY_CYCLE',
]:
# These features are PLL registers, so ignore the base
continue
print(l.strip())
assert bit_offset is not None
return bit_offset
def output_registers(bit_offset):
""" Output segbits for the known PLL register space.
The first bit offset in the register space is required to generate this
output.
"""
reg = RegisterAddress(frame_offsets=[28, 29], bit_offset=bit_offset)
for register in REGISTER_MAP:
if register is None:
for _ in range(16):
reg.next_bit()
continue
layout, register_name = register
layout_bits = REGISTER_LAYOUT[layout]
simple_layout = len(layout_bits[0]) == 2
for bit in range(16):
if register_name != layout or layout in ['CLKOUT1', 'CLKOUT2']:
print(
'CMT_UPPER_T.PLLE2.{}_{}[{}] {}'.format(
register_name, layout, bit, reg.next_bit()))
else:
print(
'CMT_UPPER_T.PLLE2.{}[{}] {}'.format(
register_name, bit, reg.next_bit()))
if False:
bit_count = 0
if simple_layout:
for field, width in layout_bits:
for bit in range(width):
print(
'CMT_UPPER_T.PLLE2.{}_{}_{}[{}] {}'.format(
register_name, layout, field, bit,
reg.next_bit()))
bit_count += 1
else:
for field, width, start_bit in layout_bits:
for bit in range(width):
print(
'CMT_UPPER_T.PLLE2.{}[{}] {}'.format(
field, start_bit + bit, reg.next_bit()))
bit_count += 1
assert bit_count == 16
def main():
parser = argparse.ArgumentParser(description="")
parser.add_argument('--seg_in')
args = parser.parse_args()
bit_offset = passthrough_non_register_segbits(args.seg_in)
output_registers(bit_offset)
if __name__ == "__main__":
main()

View File

@ -83,6 +83,7 @@ $(eval $(call fuzzer,027-bram36-config,005-tilegrid))
$(eval $(call fuzzer,028-fifo-config,005-tilegrid))
$(eval $(call fuzzer,029-bram-fifo-config,005-tilegrid))
$(eval $(call fuzzer,030-iob,005-tilegrid))
$(eval $(call fuzzer,032-cmt-pll,005-tilegrid))
$(eval $(call fuzzer,035-iob-ilogic,005-tilegrid))
$(eval $(call fuzzer,036-iob-ologic,005-tilegrid))
$(eval $(call fuzzer,040-clk-hrow-config,005-tilegrid))

View File

@ -339,6 +339,8 @@ class Segmaker:
-LIOB33 => IOB33
'''
tile_type_norm = re.sub("(_TOP|_BOT|LL|LM)?_[LR]$", "", tile_type)
tile_type_norm = re.sub(
"_TOP_[LR]_UPPER", "_UPPER", tile_type_norm)
if tile_type_norm in ['LIOB33', 'RIOB33']:
tile_type_norm = 'IOB33'

View File

@ -114,6 +114,12 @@ case "$1" in
riob33)
sed < "$2" > "$tmp1" -e 's/^IOB33\./RIOB33./' ;;
cmt_top_r_upper_t)
sed < "$2" > "$tmp1" -e 's/^CMT_UPPER_T\./CMT_TOP_R_UPPER_T./' ;;
cmt_top_l_upper_t)
sed < "$2" > "$tmp1" -e 's/^CMT_UPPER_T\./CMT_TOP_L_UPPER_T./' ;;
mask_*)
db=$XRAY_DATABASE_DIR/$XRAY_DATABASE/$1.db
ismask=true