mirror of https://github.com/openXC7/prjxray.git
Merge pull request #8 from cavearr/fuzzer/039-hclk-config-bufio-rclk2io
039-hclk-config: fuzz BUFIO_Yn.IN_USE and the BUFR RCLK2IOn leaf, with pin- and MMCM-fed sources
This commit is contained in:
commit
b9367053ae
|
|
@ -25,8 +25,33 @@ def main():
|
|||
with open('params.json') as f:
|
||||
params = json.load(f)
|
||||
|
||||
placed = {}
|
||||
try:
|
||||
with open('bufio_sites.txt') as f:
|
||||
for line in f:
|
||||
parts = line.split()
|
||||
if len(parts) == 2:
|
||||
placed[parts[0]] = parts[1]
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
used_sites = set(placed.values())
|
||||
|
||||
for row in params:
|
||||
if row.get('kind') == 'BUFIO_TILE':
|
||||
sites = row['bufio_sites']
|
||||
ys = sorted(int(st.split('Y')[-1]) for st in sites)
|
||||
y_min = ys[0] if ys else 0
|
||||
for st in sites:
|
||||
y = int(st.split('Y')[-1]) - y_min
|
||||
segmk.add_tile_tag(
|
||||
row['tile'], 'BUFIO_Y{}.IN_USE'.format(y), 1 if st in used_sites else 0)
|
||||
continue
|
||||
base_name = 'BUFR_Y{}'.format(row['y'])
|
||||
# v5: regional-clock leaf into the IOI column, per RCLK index (slot rel-y -> RCLK {0:2,1:3,2:0,3:1})
|
||||
rclk = {0: 2, 1: 3, 2: 0, 3: 1}[row['y']]
|
||||
segmk.add_tile_tag(
|
||||
row['tile'], 'HCLK_IOI_RCLK2IO{}.HCLK_IOI_CK_BUFRCLK{}'.format(rclk, rclk),
|
||||
1 if (row['IN_USE'] and row.get('consumed')) else 0)
|
||||
|
||||
segmk.add_tile_tag(
|
||||
row['tile'], '{}.IN_USE'.format(base_name), row['IN_USE'])
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@ proc run {} {
|
|||
place_design
|
||||
route_design
|
||||
|
||||
set fp [open bufio_sites.txt w]
|
||||
foreach c [get_cells -quiet -hierarchical -filter {REF_NAME == BUFIO}] {
|
||||
puts $fp "[get_property NAME $c] [get_sites -quiet -of_objects $c]"
|
||||
}
|
||||
close $fp
|
||||
write_checkpoint -force design.dcp
|
||||
write_bitstream -force design.bit
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,18 @@ def gen_sites():
|
|||
xy_fun = util.create_xy_fun('BUFR_')
|
||||
db = Database(util.get_db_root(), util.get_part())
|
||||
grid = db.grid()
|
||||
# MMCME2_ADV site per clock region: an MMCM feeding a bank's BUFIOs must sit
|
||||
# in that bank's own region (IOCLK_PLL is a region-local path). Only the
|
||||
# MMCM's CLKOUT0..3 reach BUFIO -- a PLLE2's outputs are unroutable to BUFIO
|
||||
# sites (measured), which is also why 047 registers only MMCM outputs as
|
||||
# BUFIO-capable sources.
|
||||
mmcm_by_region = {}
|
||||
for tn in grid.tiles():
|
||||
gi = grid.gridinfo_at_loc(grid.loc_of_tilename(tn))
|
||||
for st, sty in gi.sites.items():
|
||||
if sty == 'MMCME2_ADV' and gi.clock_region is not None:
|
||||
mmcm_by_region[str(gi.clock_region)] = st
|
||||
|
||||
for tile_name in sorted(grid.tiles()):
|
||||
loc = grid.loc_of_tilename(tile_name)
|
||||
gridinfo = grid.gridinfo_at_loc(loc)
|
||||
|
|
@ -28,6 +40,8 @@ def gen_sites():
|
|||
|
||||
xs = []
|
||||
ys = []
|
||||
bufio_sites = []
|
||||
bufio_xy = util.create_xy_fun('BUFIO_')
|
||||
for site, site_type in gridinfo.sites.items():
|
||||
if site_type == 'BUFR':
|
||||
x, y = xy_fun(site)
|
||||
|
|
@ -35,6 +49,9 @@ def gen_sites():
|
|||
ys.append(y)
|
||||
|
||||
sites.append((site, x, y))
|
||||
elif site_type == 'BUFIO':
|
||||
bx, by = bufio_xy(site)
|
||||
bufio_sites.append((site, bx, by))
|
||||
|
||||
if not sites:
|
||||
continue
|
||||
|
|
@ -51,6 +68,8 @@ def gen_sites():
|
|||
dx = -1
|
||||
|
||||
iobs = []
|
||||
iobs_s = []
|
||||
ilogics = []
|
||||
|
||||
for dy in (-1, -3, 2, 4):
|
||||
iob = grid.gridinfo_at_loc((loc.grid_x + dx, loc.grid_y + dy))
|
||||
|
|
@ -58,17 +77,28 @@ def gen_sites():
|
|||
for site, site_type in iob.sites.items():
|
||||
if site_type == 'IOB33M':
|
||||
iobs.append(site)
|
||||
elif site_type == 'IOB33S':
|
||||
iobs_s.append(site)
|
||||
|
||||
yield tile_name, min(xs), min(ys), sorted(sites), sorted(iobs)
|
||||
ioi = grid.gridinfo_at_loc((loc.grid_x, loc.grid_y + dy))
|
||||
for site, site_type in sorted(ioi.sites.items()):
|
||||
if site_type == 'ILOGICE3':
|
||||
ilogics.append(site)
|
||||
break
|
||||
|
||||
mmcm_site = mmcm_by_region.get(str(gridinfo.clock_region))
|
||||
yield tile_name, min(xs), min(ys), sorted(sites), sorted(iobs), sorted(bufio_sites), ilogics, mmcm_site, sorted(iobs_s)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
params_list = []
|
||||
num_clocks = 0
|
||||
num_outs = 0
|
||||
outputs = []
|
||||
luts = LutMaker()
|
||||
for tile_name, x_min, y_min, sites, iobs in gen_sites():
|
||||
for tile_name, x_min, y_min, sites, iobs, bufio_sites, ilogics, mmcm_site, iobs_s in gen_sites():
|
||||
outs_used = 0
|
||||
ioclks = []
|
||||
for iob in iobs:
|
||||
ioclk = 'clk_{}'.format(iob)
|
||||
|
|
@ -121,8 +151,36 @@ def main():
|
|||
params['CE'] = luts.get_next_output_net()
|
||||
params['CLR'] = luts.get_next_output_net()
|
||||
|
||||
outputs.append(
|
||||
'''
|
||||
params['consumed'] = 0
|
||||
if outs_used < len(iobs_s) and random.randint(0, 1):
|
||||
params['consumed'] = 1
|
||||
params['obuf_site'] = iobs_s[outs_used]
|
||||
params['out_idx'] = num_outs
|
||||
num_outs += 1
|
||||
outs_used += 1
|
||||
if params['consumed']:
|
||||
outputs.append(
|
||||
'''
|
||||
wire {site}_o;
|
||||
(* KEEP, DONT_TOUCH, LOC = "{site}" *)
|
||||
BUFR #(
|
||||
.BUFR_DIVIDE({BUFR_DIVIDE})
|
||||
) buf_{site} (
|
||||
.CE({CE}),
|
||||
.CLR({CLR}),
|
||||
.I({I}),
|
||||
.O({site}_o)
|
||||
);
|
||||
wire {site}_q;
|
||||
(* KEEP, DONT_TOUCH *)
|
||||
ODDR #(.DDR_CLK_EDGE("SAME_EDGE")) oddr_{site} (
|
||||
.C({site}_o), .CE(1'b1), .D1(1'b1), .D2(1'b0), .R(1'b0), .S(1'b0), .Q({site}_q));
|
||||
(* KEEP, DONT_TOUCH, LOC = "{obuf_site}" *)
|
||||
OBUF #(.IOSTANDARD("LVCMOS33")) obuf_{site} (.I({site}_q), .O(outs[{out_idx}]));
|
||||
'''.format(**params))
|
||||
else:
|
||||
outputs.append(
|
||||
'''
|
||||
(* KEEP, DONT_TOUCH, LOC = "{site}" *)
|
||||
BUFR #(
|
||||
.BUFR_DIVIDE({BUFR_DIVIDE})
|
||||
|
|
@ -131,14 +189,97 @@ def main():
|
|||
.CLR({CLR}),
|
||||
.I({I})
|
||||
);
|
||||
'''.format(**params))
|
||||
'''.format(**params))
|
||||
|
||||
params_list.append(params)
|
||||
|
||||
# --- BUFIO half (extension v4, fully LOC'd) ---
|
||||
rec = {}
|
||||
rec['tile'] = tile_name
|
||||
rec['kind'] = 'BUFIO_TILE'
|
||||
rec['bufio_sites'] = [site for site, _, _ in bufio_sites]
|
||||
rec['bufio_insts'] = []
|
||||
rec['bufio_src'] = {}
|
||||
rec['bufio_loc'] = {}
|
||||
bys = sorted(by for _, _, by in bufio_sites)
|
||||
by_min = bys[0] if bys else 0
|
||||
clkout_for_rel_y = {0: 0, 1: 1, 2: 2, 3: 3}
|
||||
mmcm_declared = False
|
||||
mmcm_name = 'mmcm_{}'.format(tile_name)
|
||||
for k, (iob, ioclk) in enumerate(zip(iobs, ioclks)):
|
||||
if k >= len(ilogics):
|
||||
break
|
||||
# A clock-capable IOB feeds ONE dedicated BUFIO of its bank: the k-th
|
||||
# clock-capable IOB (by y) of the tile's neighbourhood drives the k-th
|
||||
# BUFIO site (by y). Measured on every HCLK_IOI3 of xc7a100t (24/24,
|
||||
# bijective); a CCIO-fed BUFIO LOC'd anywhere else fails placement.
|
||||
bufio_by_y = sorted((st for st, _, _ in bufio_sites), key=lambda st: int(st.split('Y')[-1]))
|
||||
iobs_by_y = sorted(iobs, key=lambda st: int(st.split('Y')[-1]))
|
||||
rank = iobs_by_y.index(iob)
|
||||
if rank >= len(bufio_by_y):
|
||||
continue
|
||||
bsite = bufio_by_y[rank]
|
||||
state = random.choice(('unused', 'CCIO', 'MMCM')) if mmcm_site else random.choice(('unused', 'CCIO'))
|
||||
if state == 'unused':
|
||||
continue
|
||||
inst = 'bufio_{}'.format(iob)
|
||||
rec['bufio_insts'].append(inst)
|
||||
rec['bufio_src'][inst] = state
|
||||
rec['bufio_loc'][inst] = bsite
|
||||
if state == 'MMCM':
|
||||
if not mmcm_declared:
|
||||
mmcm_declared = True
|
||||
outputs.append(
|
||||
'''
|
||||
wire {pll}_fb, {pll}_out0, {pll}_out1, {pll}_out2, {pll}_out3;
|
||||
(* KEEP, DONT_TOUCH, LOC = "{mmcm_site}" *)
|
||||
MMCME2_BASE #(
|
||||
.CLKIN1_PERIOD(10.0), .CLKFBOUT_MULT_F(8.0), .DIVCLK_DIVIDE(1),
|
||||
.CLKOUT0_DIVIDE_F(8.0), .CLKOUT1_DIVIDE(8), .CLKOUT2_DIVIDE(8), .CLKOUT3_DIVIDE(8)
|
||||
) {pll} (
|
||||
.CLKIN1({clkin}), .CLKFBIN({pll}_fb), .CLKFBOUT({pll}_fb),
|
||||
.CLKOUT0({pll}_out0), .CLKOUT1({pll}_out1), .CLKOUT2({pll}_out2), .CLKOUT3({pll}_out3),
|
||||
.RST(1'b0), .PWRDWN(1'b0)
|
||||
);'''.format(pll=mmcm_name, clkin=ioclks[0], mmcm_site=mmcm_site))
|
||||
rel_y = int(bsite.split('Y')[-1]) - by_min
|
||||
bufio_i = '{}_out{}'.format(mmcm_name, clkout_for_rel_y[rel_y])
|
||||
else:
|
||||
bufio_i = ioclk
|
||||
outputs.append(
|
||||
'''
|
||||
wire {inst}_o;
|
||||
(* KEEP, DONT_TOUCH, LOC = "{bsite}" *)
|
||||
BUFIO {inst} (
|
||||
.I({bufio_i}),
|
||||
.O({inst}_o)
|
||||
);
|
||||
(* KEEP, DONT_TOUCH, LOC = "{ilogic}" *)
|
||||
ISERDESE2 #(
|
||||
.DATA_RATE("SDR"),
|
||||
.DATA_WIDTH(4),
|
||||
.INTERFACE_TYPE("OVERSAMPLE"),
|
||||
.IOBDELAY("NONE"),
|
||||
.NUM_CE(2),
|
||||
.SERDES_MODE("MASTER")
|
||||
) iserdes_{inst} (
|
||||
.CLK({inst}_o),
|
||||
.CLKB(),
|
||||
.CLKDIV(),
|
||||
.D(1'b0),
|
||||
.DDLY(),
|
||||
.OFB(),
|
||||
.OCLKB(),
|
||||
.RST(),
|
||||
.SHIFTIN1(),
|
||||
.SHIFTIN2()
|
||||
);
|
||||
'''.format(inst=inst, bufio_i=bufio_i, bsite=bsite, ilogic=ilogics[k]))
|
||||
params_list.append(rec)
|
||||
|
||||
print(
|
||||
'''
|
||||
module top(input [{n1}:0] clks);
|
||||
'''.format(n1=num_clocks - 1))
|
||||
module top(input [{n1}:0] clks, output [{n2}:0] outs);
|
||||
'''.format(n1=num_clocks - 1, n2=max(num_outs - 1, 0)))
|
||||
|
||||
print("""
|
||||
(* KEEP, DONT_TOUCH *)
|
||||
|
|
|
|||
Loading…
Reference in New Issue