From bf817f3548523ec7798ec308c9971f4a58dd11a7 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Mon, 4 Feb 2019 14:09:51 -0800 Subject: [PATCH] Add fuzzer for BRAM/FIFO enable bits. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fuzzers/029-bram-fifo-config/Makefile | 23 ++ fuzzers/029-bram-fifo-config/bits.dbf | 2 + fuzzers/029-bram-fifo-config/generate.py | 42 +++ fuzzers/029-bram-fifo-config/generate.tcl | 17 + fuzzers/029-bram-fifo-config/merge_in_use.py | 63 ++++ fuzzers/029-bram-fifo-config/top.py | 325 +++++++++++++++++++ fuzzers/060-bram-cascades/top.py | 48 +-- fuzzers/Makefile | 1 + prjxray/lut_maker.py | 45 +++ 9 files changed, 519 insertions(+), 47 deletions(-) create mode 100644 fuzzers/029-bram-fifo-config/Makefile create mode 100644 fuzzers/029-bram-fifo-config/bits.dbf create mode 100644 fuzzers/029-bram-fifo-config/generate.py create mode 100644 fuzzers/029-bram-fifo-config/generate.tcl create mode 100644 fuzzers/029-bram-fifo-config/merge_in_use.py create mode 100644 fuzzers/029-bram-fifo-config/top.py create mode 100644 prjxray/lut_maker.py diff --git a/fuzzers/029-bram-fifo-config/Makefile b/fuzzers/029-bram-fifo-config/Makefile new file mode 100644 index 00000000..b4482201 --- /dev/null +++ b/fuzzers/029-bram-fifo-config/Makefile @@ -0,0 +1,23 @@ +N ?= 5 + +include ../fuzzer.mk + +database: build/segbits_bramx.db + +build/segbits_bramx.rdb: $(SPECIMENS_OK) + ${XRAY_SEGMATCH} -o build/segbits_bramx.rdb $(addsuffix /segdata_bram_[lr].txt,$(SPECIMENS)) + +build/segbits_bramx.db: build/segbits_bramx.rdb + ${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf \ + --seg-fn-in build/segbits_bramx.rdb \ + --seg-fn-out build/segbits_bramx.unmerged.db + python3 merge_in_use.py build/segbits_bramx.unmerged.db > build/segbits_bramx.db + ${XRAY_MASKMERGE} build/mask_bramx.db $(addsuffix /segdata_bram_[lr].txt,$(SPECIMENS)) + +pushdb: + ${XRAY_MERGEDB} bram_l build/segbits_bramx.db + ${XRAY_MERGEDB} bram_r build/segbits_bramx.db + ${XRAY_MERGEDB} mask_bram_l build/mask_bramx.db + ${XRAY_MERGEDB} mask_bram_r build/mask_bramx.db + +.PHONY: database pushdb diff --git a/fuzzers/029-bram-fifo-config/bits.dbf b/fuzzers/029-bram-fifo-config/bits.dbf new file mode 100644 index 00000000..4f5f5aba --- /dev/null +++ b/fuzzers/029-bram-fifo-config/bits.dbf @@ -0,0 +1,2 @@ +27_150,BRAM.RAMB18_Y0.RAM_IN_USE +27_169,BRAM.RAMB18_Y1.RAM_IN_USE diff --git a/fuzzers/029-bram-fifo-config/generate.py b/fuzzers/029-bram-fifo-config/generate.py new file mode 100644 index 00000000..502cdaf4 --- /dev/null +++ b/fuzzers/029-bram-fifo-config/generate.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +import json + +from prjxray.segmaker import Segmaker + + +def bitfilter(frame, bit): + # Filter out interconnect bits. + if frame in [0, 1, 17, 23, 25]: + return False + + # ZINV_RSTRAMARSTRAM seems hard to de-correlate from FIFO_Y0_IN_USE. + if (frame, bit) == (27, 116): + return False + + return True + +def main(): + segmk = Segmaker("design.bits") + + print("Loading tags") + with open('params.json') as f: + params = json.load(f) + + for tile_param in params: + for param, tag in ( + ('Y0_IN_USE', 'RAMB18_Y0.IN_USE'), + ('Y1_IN_USE', 'RAMB18_Y1.IN_USE'), + ('RAMB_Y0_IN_USE', 'RAMB18_Y0.RAM_IN_USE'), + ('RAMB_Y1_IN_USE', 'RAMB18_Y1.RAM_IN_USE'), + ('FIFO_Y0_IN_USE', 'RAMB18_Y0.FIFO_IN_USE'), + ('FIFO_Y1_IN_USE', 'RAMB18_Y1.FIFO_IN_USE'), + ): + segmk.add_tile_tag(tile_param['tile'], tag, tile_param[param]) + + segmk.compile(bitfilter=bitfilter) + segmk.write() + + +if __name__ == '__main__': + main() diff --git a/fuzzers/029-bram-fifo-config/generate.tcl b/fuzzers/029-bram-fifo-config/generate.tcl new file mode 100644 index 00000000..3044e100 --- /dev/null +++ b/fuzzers/029-bram-fifo-config/generate.tcl @@ -0,0 +1,17 @@ +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] + + place_design + route_design + + write_checkpoint -force design.dcp + write_bitstream -force design.bit +} + +run diff --git a/fuzzers/029-bram-fifo-config/merge_in_use.py b/fuzzers/029-bram-fifo-config/merge_in_use.py new file mode 100644 index 00000000..4a8b05b5 --- /dev/null +++ b/fuzzers/029-bram-fifo-config/merge_in_use.py @@ -0,0 +1,63 @@ +""" Merge IN_USE entries. + +Segmatch has trouble directly solving for RAM_IN_USE and FIFO_IN_USE. +Instead if solves for IN_USE (e.g. RAM_IN_USE or FIFO_IN_USE) and the FIFO_IN_USE bit. + +This tool merges the 3 entries into 2 entries RAM_IN_USE and FIFO_IN_USE. + + +BRAM.RAMB18_Y0.FIFO_IN_USE 27_150 +BRAM.RAMB18_Y0.IN_USE 27_100 27_99 +BRAM.RAMB18_Y0.RAM_IN_USE !27_150 + +becomes + +BRAM.RAMB18_Y0.FIFO_IN_USE 27_100 27_99 27_150 +BRAM.RAMB18_Y0.RAM_IN_USE 27_100 27_99 !27_150 + +""" +import argparse + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + + parser.add_argument('unmerged_db') + + args = parser.parse_args() + + site_parts = {} + + with open(args.unmerged_db) as f: + for l in f: + parts = l.strip().split(' ') + + feature = parts[0] + bits = parts[1:] + + feature_parts = feature.split('.') + + assert feature_parts[0] == 'BRAM' + assert feature_parts[1] in ('RAMB18_Y0', 'RAMB18_Y1') + site = feature_parts[1] + + if site not in site_parts: + site_parts[site] = {} + + site_parts[site][feature_parts[2]] = bits + + assert len(site_parts) == 2 + for site in site_parts: + assert 'IN_USE' in site_parts[site] + assert len(site_parts[site]) == 3 + + for feature in site_parts[site]: + if feature == 'IN_USE': + continue + + print('BRAM.{site}.{feature} {bits}'.format( + site=site, + feature=feature, + bits=' '.join(site_parts[site]['IN_USE'] + site_parts[site][feature]))) + +if __name__ == "__main__": + main() diff --git a/fuzzers/029-bram-fifo-config/top.py b/fuzzers/029-bram-fifo-config/top.py new file mode 100644 index 00000000..f7c6b797 --- /dev/null +++ b/fuzzers/029-bram-fifo-config/top.py @@ -0,0 +1,325 @@ +import json +import os +import random +random.seed(int(os.getenv("SEED"), 16)) +from prjxray import util +from prjxray import verilog +from prjxray.db import Database +from prjxray.lut_maker import LutMaker + +WRITE_MODES = ("WRITE_FIRST", "NO_CHANGE", "READ_FIRST") + +def gen_sites(): + 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) + + if gridinfo.tile_type not in ['BRAM_L', 'BRAM_R']: + continue + + sites = {} + for site_name, site_type in gridinfo.sites.items(): + sites[site_type] = site_name + + yield tile_name, sites + +def ramb18(tile_name, luts, lines, sites): + """ RAMB18E1 in either top or bottom site. """ + + params = {} + params['tile'] = tile_name + params['Y0_IN_USE'] = random.randint(0, 1) == 1 + params['Y1_IN_USE'] = not params['Y0_IN_USE'] + params['RAMB_Y0_IN_USE'] = params['Y0_IN_USE'] + params['RAMB_Y1_IN_USE'] = params['Y1_IN_USE'] + params['FIFO_Y0_IN_USE'] = False + params['FIFO_Y1_IN_USE'] = False + + if params['Y0_IN_USE']: + site = sites['FIFO18E1'] + elif params['Y1_IN_USE']: + site = sites['RAMB18E1'] + else: + assert False + + lines.append( + ''' + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + RAMB18E1 #( + ) bram_{site} ( + ); + '''.format( + site=site)) + + return params + +def ramb18_2x(tile_name, luts, lines, sites): + """ RAMB18E1 in both top and bottom site. """ + + params = {} + params['tile'] = tile_name + params['Y0_IN_USE'] = True + params['Y1_IN_USE'] = True + params['RAMB_Y0_IN_USE'] = True + params['RAMB_Y1_IN_USE'] = True + params['FIFO_Y0_IN_USE'] = False + params['FIFO_Y1_IN_USE'] = False + + lines.append( + ''' + (* KEEP, DONT_TOUCH, LOC = "{top_site}" *) + RAMB18E1 #( + ) bram_{top_site} ( + ); + (* KEEP, DONT_TOUCH, LOC = "{bottom_site}" *) + RAMB18E1 #( + ) bram_{bottom_site} ( + ); + '''.format( + top_site=sites['FIFO18E1'], + bottom_site=sites['RAMB18E1'], + )) + + return params + +def ramb36(tile_name, luts, lines, sites): + """ RAMB36 consuming entire tile. """ + + params = {} + params['tile'] = tile_name + params['Y0_IN_USE'] = True + params['Y1_IN_USE'] = True + params['RAMB_Y0_IN_USE'] = True + params['RAMB_Y1_IN_USE'] = True + params['FIFO_Y0_IN_USE'] = False + params['FIFO_Y1_IN_USE'] = False + + site = sites['RAMBFIFO36E1'] + + lines.append( + """ + wire [7:0] webwe_{site}; + wire [3:0] wea_{site}; + wire regce_{site}; + wire [15:0] rdaddr_{site}; + wire [15:0] wraddr_{site}; + """.format(site=site)) + + for bit in range(15): + lines.append('assign rdaddr_{site}[{bit}] = {net};'.format(bit=bit, site=site,net=luts.get_next_output_net())) + lines.append('assign wraddr_{site}[{bit}] = {net};'.format(bit=bit, site=site,net=luts.get_next_output_net())) + + for bit in range(8): + lines.append('assign webwe_{site}[{bit}] = {net};'.format(bit=bit, site=site,net=luts.get_next_output_net())) + + for bit in range(4): + lines.append('assign wea_{site}[{bit}] = {net};'.format(bit=bit, site=site,net=luts.get_next_output_net())) + + lines.append('assign regce_{site} = {net};'.format(bit=bit, site=site,net=luts.get_next_output_net())) + + do_reg = verilog.vrandbit() + ram_mode = random.choice(('SDP', 'TDP')) + READ_WIDTH_A = 0 + READ_WIDTH_B = 0 + + if ram_mode == 'TDP': + write_mode_a = random.choice(WRITE_MODES) + write_mode_b = random.choice(WRITE_MODES) + WRITE_WIDTH_A = 36 + WRITE_WIDTH_B = 36 + else: + write_mode_a = 'WRITE_FIRST' + write_mode_b = 'WRITE_FIRST' + WRITE_WIDTH_A = 72 + WRITE_WIDTH_B = 72 + + lines.append( + ''' + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + RAMB36E1 #( + .DOA_REG({doa_reg}), + .DOB_REG({dob_reg}), + .WRITE_MODE_A({write_mode_a}), + .WRITE_MODE_B({write_mode_b}), + .READ_WIDTH_A({READ_WIDTH_A}), + .READ_WIDTH_B({READ_WIDTH_B}), + .WRITE_WIDTH_A({WRITE_WIDTH_A}), + .WRITE_WIDTH_B({WRITE_WIDTH_B}), + .INIT_A(36'hFF_FFFF_FFFF), + .SRVAL_A(36'hFF_FFFF_FFFF), + .INIT_B(36'hFF_FFFF_FFFF), + .SRVAL_B(36'hFF_FFFF_FFFF), + .RAM_MODE({ram_mode}) + ) bram_{site} ( + .ADDRARDADDR(rdaddr_{site}), + .ADDRBWRADDR(wraddr_{site}), + .REGCEAREGCE(regce_{site}), + .REGCEB(regce_{site}), + .WEBWE(webwe_{site}), + .WEA(wea_{site}) + ); + '''.format( + site=site, + doa_reg=do_reg, + dob_reg=do_reg, + write_mode_a=verilog.quote(write_mode_a), + write_mode_b=verilog.quote(write_mode_b), + ram_mode=verilog.quote(ram_mode), + READ_WIDTH_A=READ_WIDTH_A, + READ_WIDTH_B=READ_WIDTH_B, + WRITE_WIDTH_A=WRITE_WIDTH_A, + WRITE_WIDTH_B=WRITE_WIDTH_B, + )) + + return params + +def fifo18(tile_name, luts, lines, sites): + """ FIFO18E1 without bottom RAMB site. """ + + params = {} + params['tile'] = tile_name + params['Y0_IN_USE'] = True + params['Y1_IN_USE'] = False + params['RAMB_Y0_IN_USE'] = False + params['RAMB_Y1_IN_USE'] = False + params['FIFO_Y0_IN_USE'] = True + params['FIFO_Y1_IN_USE'] = False + + lines.append( + ''' + wire fifo_rst_{site}; + (* KEEP, DONT_TOUCH *) + LUT6 fifo_lut_{site} ( + .O(fifo_rst_{site}) + ); + + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + FIFO18E1 #( + .ALMOST_EMPTY_OFFSET(8), + .ALMOST_FULL_OFFSET(8), + .DATA_WIDTH({data_width}) + ) fifo_{site} ( + .RST(fifo_rst_{site}) + ); + '''.format( + site=sites['FIFO18E1'], + data_width=random.choice((4, 9)), + )) + + return params + + +def fifo18_ramb18(tile_name, luts, lines, sites): + """ FIFO18E1 in top site and RAMB18E1 in bottom site. """ + + params = {} + params['tile'] = tile_name + params['Y0_IN_USE'] = True + params['Y1_IN_USE'] = True + params['RAMB_Y0_IN_USE'] = False + params['RAMB_Y1_IN_USE'] = True + params['FIFO_Y0_IN_USE'] = True + params['FIFO_Y1_IN_USE'] = False + + lines.append( + ''' + wire fifo_rst_{fifo_site}; + (* KEEP, DONT_TOUCH *) + LUT6 fifo_lut_{fifo_site} ( + .O(fifo_rst_{fifo_site}) + ); + + (* KEEP, DONT_TOUCH, LOC = "{fifo_site}" *) + FIFO18E1 #( + .ALMOST_EMPTY_OFFSET(5), + .ALMOST_FULL_OFFSET(5) + ) fifo_{fifo_site} ( + .RST(fifo_rst_{fifo_site}) + ); + + (* KEEP, DONT_TOUCH, LOC = "{ramb_site}" *) + RAMB18E1 #( + ) bram_{ramb_site} ( + ); + '''.format( + fifo_site=sites['FIFO18E1'], + ramb_site=sites['RAMB18E1'], + )) + + return params + +def fifo36(tile_name, luts, lines, sites): + """ FIFO36E1 consuming entire tile. """ + + params = {} + params['tile'] = tile_name + params['Y0_IN_USE'] = True + params['Y1_IN_USE'] = True + params['RAMB_Y0_IN_USE'] = False + params['RAMB_Y1_IN_USE'] = False + params['FIFO_Y0_IN_USE'] = True + params['FIFO_Y1_IN_USE'] = True + + data_width = random.choice((4, 9)) + if data_width == 4: + ALMOST_EMPTY_OFFSET = 8184 + ALMOST_FULL_OFFSET = 8184 + else: + ALMOST_EMPTY_OFFSET = 4087 + ALMOST_FULL_OFFSET = 4087 + + lines.append( + ''' + wire fifo_rst_{site}; + (* KEEP, DONT_TOUCH *) + LUT6 fifo_lut_{site} ( + .O(fifo_rst_{site}) + ); + + (* KEEP, DONT_TOUCH, LOC = "{site}" *) + FIFO36E1 #( + .ALMOST_EMPTY_OFFSET({ALMOST_EMPTY_OFFSET}), + .ALMOST_FULL_OFFSET({ALMOST_FULL_OFFSET}), + .DATA_WIDTH({data_width}), + .INIT(36'hFF_FFFF_FFFF), + .SRVAL(36'hFF_FFFF_FFFF) + ) fifo_{site} ( + .RST(fifo_rst_{site}) + ); + '''.format( + site=sites['RAMBFIFO36E1'], + data_width=data_width, + ALMOST_EMPTY_OFFSET=ALMOST_EMPTY_OFFSET, + ALMOST_FULL_OFFSET=ALMOST_FULL_OFFSET)) + + return params + +def main(): + print(''' +module top(); + ''') + + luts = LutMaker() + lines = [] + + params_list = [] + for tile_name, sites in gen_sites(): + gen_fun = random.choice((ramb18, ramb18_2x, ramb36, fifo18, fifo18_ramb18, fifo36)) + params_list.append(gen_fun(tile_name, luts, lines, sites)) + + for lut in luts.create_wires_and_luts(): + print(lut) + + for l in lines: + print(l) + + print("endmodule") + + with open('params.json', 'w') as f: + json.dump(params_list, f, indent=2) + + +if __name__ == '__main__': + main() diff --git a/fuzzers/060-bram-cascades/top.py b/fuzzers/060-bram-cascades/top.py index 5b96e01c..4d4c0ad7 100644 --- a/fuzzers/060-bram-cascades/top.py +++ b/fuzzers/060-bram-cascades/top.py @@ -3,6 +3,7 @@ import sys import random import math from prjxray import util +from prjxray.lut_maker import LutMaker from prjxray.db import Database random.seed(int(os.getenv("SEED"), 16)) @@ -23,53 +24,6 @@ def bram_count(): return count -class LutMaker(object): - def __init__(self): - self.input_lut_idx = 0 - self.output_lut_idx = 0 - self.lut_input_idx = 0 - - def get_next_input_net(self): - net = 'lut_{}_i[{}]'.format(self.input_lut_idx, self.lut_input_idx) - - self.lut_input_idx += 1 - if self.lut_input_idx > 5: - self.lut_input_idx = 0 - self.input_lut_idx += 1 - - return net - - def get_next_output_net(self): - net = 'lut_{}_o'.format(self.output_lut_idx) - self.output_lut_idx += 1 - return net - - def create_wires_and_luts(self): - if self.output_lut_idx > self.input_lut_idx: - nluts = self.output_lut_idx - else: - nluts = self.input_lut_idx - if self.lut_input_idx > 0: - nluts += 1 - - for lut in range(nluts): - yield """ - wire [5:0] lut_{lut}_i; - wire lut_{lut}_o; - - (* KEEP, DONT_TOUCH *) - LUT6 lut_{lut} ( - .I0(lut_{lut}_i[0]), - .I1(lut_{lut}_i[1]), - .I2(lut_{lut}_i[2]), - .I3(lut_{lut}_i[3]), - .I4(lut_{lut}_i[4]), - .I5(lut_{lut}_i[5]), - .O(lut_{lut}_o) - ); - """.format(lut=lut) - - def sdp_bram(name, width, address_bits): depth = 2**address_bits diff --git a/fuzzers/Makefile b/fuzzers/Makefile index f1001003..be8c70d8 100644 --- a/fuzzers/Makefile +++ b/fuzzers/Makefile @@ -42,6 +42,7 @@ $(eval $(call fuzzer,025-bram-config,005-tilegrid)) $(eval $(call fuzzer,026-bram-data,005-tilegrid)) $(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,050-pip-seed,005-tilegrid)) $(eval $(call fuzzer,051-pip-imuxlout-bypalts,050-pip-seed)) $(eval $(call fuzzer,052-pip-clkin,050-pip-seed)) diff --git a/prjxray/lut_maker.py b/prjxray/lut_maker.py new file mode 100644 index 00000000..905c2e9e --- /dev/null +++ b/prjxray/lut_maker.py @@ -0,0 +1,45 @@ +class LutMaker(object): + def __init__(self): + self.input_lut_idx = 0 + self.output_lut_idx = 0 + self.lut_input_idx = 0 + + def get_next_input_net(self): + net = 'lut_{}_i[{}]'.format(self.input_lut_idx, self.lut_input_idx) + + self.lut_input_idx += 1 + if self.lut_input_idx > 5: + self.lut_input_idx = 0 + self.input_lut_idx += 1 + + return net + + def get_next_output_net(self): + net = 'lut_{}_o'.format(self.output_lut_idx) + self.output_lut_idx += 1 + return net + + def create_wires_and_luts(self): + if self.output_lut_idx > self.input_lut_idx: + nluts = self.output_lut_idx + else: + nluts = self.input_lut_idx + if self.lut_input_idx > 0: + nluts += 1 + + for lut in range(nluts): + yield """ + wire [5:0] lut_{lut}_i; + wire lut_{lut}_o; + + (* KEEP, DONT_TOUCH *) + LUT6 lut_{lut} ( + .I0(lut_{lut}_i[0]), + .I1(lut_{lut}_i[1]), + .I2(lut_{lut}_i[2]), + .I3(lut_{lut}_i[3]), + .I4(lut_{lut}_i[4]), + .I5(lut_{lut}_i[5]), + .O(lut_{lut}_o) + ); + """.format(lut=lut)