From 3059607dd799e18d5ce22c536021fc690d3ac13a Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 30 Oct 2017 11:32:17 +0000 Subject: PLL configuration fuzzing script --- icefuzz/tests/pllauto/.gitignore | 1 + icefuzz/tests/pllauto/pll_data_up5k.txt | 39 +++++ icefuzz/tests/pllauto/pllauto.py | 276 ++++++++++++++++++++++++++++++++ 3 files changed, 316 insertions(+) create mode 100644 icefuzz/tests/pllauto/.gitignore create mode 100644 icefuzz/tests/pllauto/pll_data_up5k.txt create mode 100755 icefuzz/tests/pllauto/pllauto.py diff --git a/icefuzz/tests/pllauto/.gitignore b/icefuzz/tests/pllauto/.gitignore new file mode 100644 index 0000000..7fafebe --- /dev/null +++ b/icefuzz/tests/pllauto/.gitignore @@ -0,0 +1 @@ +work_pllauto/ \ No newline at end of file diff --git a/icefuzz/tests/pllauto/pll_data_up5k.txt b/icefuzz/tests/pllauto/pll_data_up5k.txt new file mode 100644 index 0000000..2010989 --- /dev/null +++ b/icefuzz/tests/pllauto/pll_data_up5k.txt @@ -0,0 +1,39 @@ +"PLLTYPE_1": (14, 31, "PLLCONFIG_1"), +"PLLTYPE_2": (14, 31, "PLLCONFIG_3"), +"PLLTYPE_0": (12, 31, "PLLCONFIG_5"), +"FEEDBACK_PATH_0": (14, 31, "PLLCONFIG_5"), +"FEEDBACK_PATH_1": (11, 31, "PLLCONFIG_9"), +"FEEDBACK_PATH_2": (12, 31, "PLLCONFIG_1"), +"PLLOUT_SELECT_A_0": (12, 31, "PLLCONFIG_6"), +"PLLOUT_SELECT_A_1": (12, 31, "PLLCONFIG_7"), +"PLLOUT_SELECT_B_0": (12, 31, "PLLCONFIG_2"), +"PLLOUT_SELECT_B_1": (12, 31, "PLLCONFIG_3"), +"SHIFTREG_DIV_MODE": (12, 31, "PLLCONFIG_4"), +"FDA_FEEDBACK_0": (12, 31, "PLLCONFIG_9"), +"FDA_FEEDBACK_1": (13, 31, "PLLCONFIG_1"), +"FDA_FEEDBACK_2": (13, 31, "PLLCONFIG_2"), +"FDA_FEEDBACK_3": (13, 31, "PLLCONFIG_3"), +"FDA_RELATIVE_0": (13, 31, "PLLCONFIG_5"), +"FDA_RELATIVE_1": (13, 31, "PLLCONFIG_6"), +"FDA_RELATIVE_2": (13, 31, "PLLCONFIG_7"), +"FDA_RELATIVE_3": (13, 31, "PLLCONFIG_8"), +"DIVR_0": (10, 31, "PLLCONFIG_1"), +"DIVR_1": (10, 31, "PLLCONFIG_2"), +"DIVR_2": (10, 31, "PLLCONFIG_3"), +"DIVR_3": (10, 31, "PLLCONFIG_4"), +"DIVF_0": (10, 31, "PLLCONFIG_5"), +"DIVF_1": (10, 31, "PLLCONFIG_6"), +"DIVF_2": (10, 31, "PLLCONFIG_7"), +"DIVF_3": (10, 31, "PLLCONFIG_8"), +"DIVF_4": (10, 31, "PLLCONFIG_9"), +"DIVF_5": (11, 31, "PLLCONFIG_1"), +"DIVF_6": (11, 31, "PLLCONFIG_2"), +"DIVQ_0": (11, 31, "PLLCONFIG_3"), +"DIVQ_1": (11, 31, "PLLCONFIG_4"), +"DIVQ_2": (11, 31, "PLLCONFIG_5"), +"FILTER_RANGE_0": (11, 31, "PLLCONFIG_6"), +"FILTER_RANGE_1": (11, 31, "PLLCONFIG_7"), +"FILTER_RANGE_2": (11, 31, "PLLCONFIG_8"), +"TEST_MODE": (12, 31, "PLLCONFIG_8"), +"DELAY_ADJMODE_FB": (13, 31, "PLLCONFIG_4"), +"DELAY_ADJMODE_REL": (13, 31, "PLLCONFIG_9"), diff --git a/icefuzz/tests/pllauto/pllauto.py b/icefuzz/tests/pllauto/pllauto.py new file mode 100755 index 0000000..647be29 --- /dev/null +++ b/icefuzz/tests/pllauto/pllauto.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python3 + +import os, sys +# PLL automatic fuzzing script (WIP) + +device = "up5k" + +# PLL config bits to be fuzzed +# These must be in an order such that a config with bit i set doesn't set any other undiscovered bits yet +# e.g. PLL_TYPE must be fuzzed first as these will need to be set later on by virtue of enabling the PLL + +fuzz_bits = [ + "PLLTYPE_1", + "PLLTYPE_2", + "PLLTYPE_0", #NB: as per the rule above this comes later is it can only be set by also setting 1 or 2 + + "FEEDBACK_PATH_0", + "FEEDBACK_PATH_1", + "FEEDBACK_PATH_2", + + "PLLOUT_SELECT_A_0", + "PLLOUT_SELECT_A_1", + + "PLLOUT_SELECT_B_0", + "PLLOUT_SELECT_B_1", + + "SHIFTREG_DIV_MODE", + + "FDA_FEEDBACK_0", + "FDA_FEEDBACK_1", + "FDA_FEEDBACK_2", + "FDA_FEEDBACK_3", + + "FDA_RELATIVE_0", + "FDA_RELATIVE_1", + "FDA_RELATIVE_2", + "FDA_RELATIVE_3", + + "DIVR_0", + "DIVR_1", + "DIVR_2", + "DIVR_3", + + "DIVF_0", + "DIVF_1", + "DIVF_2", + "DIVF_3", + "DIVF_4", + "DIVF_5", + "DIVF_6", + + #DIVQ_0 is missing, see comments later on + "DIVQ_1", + "DIVQ_2", + + "FILTER_RANGE_0", + "FILTER_RANGE_1", + "FILTER_RANGE_2", + + "TEST_MODE", + + "DELAY_ADJMODE_FB", #these come at the end in case they set FDA_RELATIVE?? + "DELAY_ADJMODE_REL" +] + +# Boilerplate code based on the icefuzz script +code_prefix = """ +module top(packagepin, a, b, w, x, y, z, extfeedback, bypass, resetb, lock, latchinputvalue, sdi, sdo, sclk, dynamicdelay_0, dynamicdelay_1, dynamicdelay_2, dynamicdelay_3, dynamicdelay_4, dynamicdelay_5, dynamicdelay_6, dynamicdelay_7); +input packagepin; +input a; +input b; +output w; +output x; +output reg y; +output reg z; +input extfeedback; +input bypass; +input resetb; +output lock; +input latchinputvalue; +input sdi; +output sdo; +input sclk; +wire plloutcorea; +wire plloutcoreb; +wire plloutglobala; +wire plloutglobalb; +assign w = plloutcorea ^ a; +assign x = plloutcoreb ^ b; +always @(posedge plloutglobala) y <= a; +always @(posedge plloutglobalb) z <= b; +input dynamicdelay_0; +input dynamicdelay_1; +input dynamicdelay_2; +input dynamicdelay_3; +input dynamicdelay_4; +input dynamicdelay_5; +input dynamicdelay_6; +input dynamicdelay_7; +""" + +def get_param_value(param_name, param_size, fuzz_bit): + param = str(param_size) + "'b"; + for i in range(param_size - 1, -1, -1): + if fuzz_bit == param_name + "_" + str(i): + param += '1' + else: + param += '0' + return param +def inst_pll(fuzz_bit): + pll_type = "SB_PLL40_2F_PAD" #default to this as it's most flexible + + if fuzz_bit == "PLLTYPE_0": + pll_type = "SB_PLL40_CORE" + elif fuzz_bit == "PLLTYPE_1": + pll_type = "SB_PLL40_PAD" + elif fuzz_bit == "PLLTYPE_2": + pll_type = "SB_PLL40_2_PAD" + + v = pll_type + " pll_inst (\n" + if pll_type == "SB_PLL40_CORE": + v += "\t.REFERENCECLK(referenceclk), \n" + else: + v += "\t.PACKAGEPIN(packagepin), \n" + v += "\t.RESETB(resetb),\n" + v += "\t.BYPASS(bypass),\n" + v += "\t.EXTFEEDBACK(extfeedback),\n" + v += "\t.LOCK(lock),\n" + v += "\t.LATCHINPUTVALUE(latchinputvalue),\n" + v += "\t.SDI(sdi),\n" + v += "\t.SDO(sdo),\n" + v += "\t.SCLK(sclk),\n" + if pll_type == "SB_PLL40_2F_PAD" or pll_type == "SB_PLL40_2_PAD": + v += "\t.PLLOUTCOREA(plloutcorea),\n" + v += "\t.PLLOUTGLOBALA(plloutglobala),\n" + v += "\t.PLLOUTCOREB(plloutcoreb),\n" + v += "\t.PLLOUTGLOBALB(plloutglobalb),\n" + else: + v += "\t.PLLOUTCORE(plloutcorea),\n" + v += "\t.PLLOUTGLOBAL(plloutglobala),\n" + v += "\t.DYNAMICDELAY({dynamicdelay_7, dynamicdelay_6, dynamicdelay_5, dynamicdelay_4, dynamicdelay_3, dynamicdelay_2, dynamicdelay_1, dynamicdelay_0})\n" + v += ");\n" + + v += "defparam pll_inst.DIVR = " + get_param_value("DIVR", 4, fuzz_bit) + ";\n" + v += "defparam pll_inst.DIVF = " + get_param_value("DIVF", 7, fuzz_bit) + ";\n" + v += "defparam pll_inst.DIVQ = " + get_param_value("DIVQ", 3, fuzz_bit) + ";\n" + v += "defparam pll_inst.FILTER_RANGE = " + get_param_value("FILTER_RANGE", 3, fuzz_bit) + ";\n" + + if fuzz_bit == "FEEDBACK_PATH_0": + v += "defparam pll_inst.FEEDBACK_PATH = \"SIMPLE\";\n" + elif fuzz_bit == "FEEDBACK_PATH_1": + v += "defparam pll_inst.FEEDBACK_PATH = \"PHASE_AND_DELAY\";\n" + elif fuzz_bit == "FEEDBACK_PATH_2": + v += "defparam pll_inst.FEEDBACK_PATH = \"EXTERNAL\";\n" + else: + v += "defparam pll_inst.FEEDBACK_PATH = \"DELAY\";\n" + + v += "defparam pll_inst.DELAY_ADJUSTMENT_MODE_FEEDBACK = \"" + ("DYNAMIC" if (fuzz_bit == "DELAY_ADJMODE_FB") else "FIXED") + "\";\n" + v += "defparam pll_inst.FDA_FEEDBACK = " + get_param_value("FDA_FEEDBACK", 4, fuzz_bit) + ";\n" + v += "defparam pll_inst.DELAY_ADJUSTMENT_MODE_RELATIVE = \"" + ("DYNAMIC" if (fuzz_bit == "DELAY_ADJMODE_REL") else "FIXED") + "\";\n" + v += "defparam pll_inst.FDA_RELATIVE = " + get_param_value("FDA_RELATIVE", 4, fuzz_bit) + ";\n" + v += "defparam pll_inst.SHIFTREG_DIV_MODE = " + ("1'b1" if (fuzz_bit == "SHIFTREG_DIV_MODE") else "1'b0") + ";\n" + + + + if pll_type == "SB_PLL40_2F_PAD" or pll_type == "SB_PLL40_2_PAD": + if pll_type == "SB_PLL40_2F_PAD": + if fuzz_bit == "PLLOUT_SELECT_A_0": + v += "defparam pll_inst.PLLOUT_SELECT_PORTA = \"GENCLK_HALF\";\n" + elif fuzz_bit == "PLLOUT_SELECT_A_1": + v += "defparam pll_inst.PLLOUT_SELECT_PORTA = \"SHIFTREG_90deg\";\n" + else: + v += "defparam pll_inst.PLLOUT_SELECT_PORTA = \"GENCLK\";\n" + if fuzz_bit == "PLLOUT_SELECT_B_0": + v += "defparam pll_inst.PLLOUT_SELECT_PORTB = \"GENCLK_HALF\";\n" + elif fuzz_bit == "PLLOUT_SELECT_B_1": + v += "defparam pll_inst.PLLOUT_SELECT_PORTB = \"SHIFTREG_90deg\";\n" + else: + v += "defparam pll_inst.PLLOUT_SELECT_PORTB = \"GENCLK\";\n" + else: + if fuzz_bit == "PLLOUT_SELECT_A_0": + v += "defparam pll_inst.PLLOUT_SELECT = \"GENCLK_HALF\";\n" + elif fuzz_bit == "PLLOUT_SELECT_A_1": + v += "defparam pll_inst.PLLOUT_SELECT = \"SHIFTREG_90deg\";\n" + else: + v += "defparam pll_inst.PLLOUT_SELECT = \"GENCLK\";\n" + v += "defparam pll_inst.TEST_MODE = " + ("1'b1" if (fuzz_bit == "TEST_MODE") else "1'b0") + ";\n" + + return v; + +def make_vlog(fuzz_bit): + vlog = code_prefix + vlog += inst_pll(fuzz_bit) + vlog += "endmodule" + return vlog + +known_bits = [] + +# Set to true to continue even if multiple bits are changed (needed because +# of the issue discusssed below) +show_all_bits = False #TODO: make this an argument + +device = "up5k" #TODO: environment variable? + +#HACK: icecube doesn't let you set all of the DIVQ bits to 0, +#which makes fuzzing early on annoying as there is never a case +#with just 1 bit set. So a tiny bit of semi-manual work is needed +#first to discover this (basically run this script with show_all_bits=True +#and look for the stuck bit) +#TODO: clever code could get rid of this +divq_bit0 = { + "up5k" : (11, 31, 3) +} + +#Return a list of PLL config bits in the format (x, y, bit) +def parse_exp(expfile): + current_x = 0 + current_y = 0 + bits = [] + with open(expfile, 'r') as f: + for line in f: + splitline = line.split(' ') + if splitline[0] == ".io_tile": + current_x = int(splitline[1]) + current_y = int(splitline[2]) + elif splitline[0] == "PLL": + if splitline[1][:10] == "PLLCONFIG_": + bitidx = int(splitline[1][10:]) + bits.append((current_x, current_y, bitidx)) + return bits + +#Convert a bit tuple as returned from the above to a nice string +def bit_to_str(bit): + return "(%d, %d, \"PLLCONFIG_%d\")" % bit + +#The main fuzzing function +def do_fuzz(): + if not os.path.exists("./work_pllauto"): + os.mkdir("./work_pllauto") + known_bits.append(divq_bit0[device]) + with open("pll_data_" + device + ".txt", 'w') as dat: + for fuzz_bit in fuzz_bits: + vlog = make_vlog(fuzz_bit) + with open("./work_pllauto/pllauto.v", 'w') as f: + f.write(vlog) + retval = os.system("bash ../../icecube.sh -" + device + " ./work_pllauto/pllauto.v > ./work_pllauto/icecube.log 2>&1") + if retval != 0: + sys.stderr.write('ERROR: icecube returned non-zero error code\n') + sys.exit(1) + retval = os.system("../../../icebox/icebox_explain.py ./work_pllauto/pllauto.asc > ./work_pllauto/pllauto.exp") + if retval != 0: + sys.stderr.write('ERROR: icebox_explain returned non-zero error code\n') + sys.exit(1) + pll_bits = parse_exp("./work_pllauto/pllauto.exp") + new_bits = [] + for set_bit in pll_bits: + if not (set_bit in known_bits): + new_bits.append(set_bit) + if len(new_bits) == 0: + sys.stderr.write('ERROR: no new bits set when setting config bit ' + fuzz_bit + '\n') + sys.exit(1) + if len(new_bits) > 1: + sys.stderr.write('ERROR: multiple new bits set when setting config bit ' + fuzz_bit + '\n') + for bit in new_bits: + sys.stderr.write('\t' + bit_to_str(bit) + '\n') + if not show_all_bits: + sys.exit(1) + if len(new_bits) == 1: + known_bits.append(new_bits[0]) + #print DIVQ_0 at the right moment, as it's not fuzzed normally + if fuzz_bit == "DIVQ_1": + print(("\"DIVQ_0\":").ljust(24) + bit_to_str(divq_bit0[device]) + ",") + dat.write(("\"DIVQ_0\":").ljust(24) + bit_to_str(divq_bit0[device]) + ",\n") + print(("\"" + fuzz_bit + "\":").ljust(24) + bit_to_str(new_bits[0]) + ",") + dat.write(("\"" + fuzz_bit + "\":").ljust(24) + bit_to_str(new_bits[0]) + ",\n") +do_fuzz() \ No newline at end of file -- cgit v1.2.3